@huyooo/ai-chat-shared 0.2.13 → 0.2.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +42 -12
- package/dist/index.js +1 -262
- package/dist/styles.css +274 -22
- package/package.json +7 -3
- package/src/index.ts +15 -2
- package/src/markdown.ts +262 -6
- package/src/parser.ts +60 -30
- package/src/styles.css +274 -22
- package/src/types.ts +0 -13
- package/src/visual.ts +68 -0
- package/dist/index.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -22,17 +22,6 @@ interface CodeBlock extends ContentBlockBase {
|
|
|
22
22
|
}
|
|
23
23
|
/** 内容块联合类型 */
|
|
24
24
|
type ContentBlock = TextBlock | CodeBlock;
|
|
25
|
-
/** 工具渲染器 Props */
|
|
26
|
-
interface ToolRendererProps {
|
|
27
|
-
/** 工具名称 */
|
|
28
|
-
toolName: string;
|
|
29
|
-
/** 工具参数 */
|
|
30
|
-
toolArgs: Record<string, unknown>;
|
|
31
|
-
/** 工具返回结果 */
|
|
32
|
-
toolResult: unknown;
|
|
33
|
-
/** 执行状态 */
|
|
34
|
-
status: 'running' | 'completed' | 'error';
|
|
35
|
-
}
|
|
36
25
|
/** 天气数据 */
|
|
37
26
|
interface WeatherData {
|
|
38
27
|
city: string;
|
|
@@ -95,6 +84,12 @@ interface StreamParseState {
|
|
|
95
84
|
codeLanguage?: string;
|
|
96
85
|
/** 当前代码块内容 */
|
|
97
86
|
codeContent: string;
|
|
87
|
+
/** 当前代码块 ID(用于稳定 key,避免流式时每次 render 都生成新 id) */
|
|
88
|
+
codeBlockId: string | null;
|
|
89
|
+
/** 当前文本块内容(流式追加,避免把一句话拆成很多 text block) */
|
|
90
|
+
textContent: string;
|
|
91
|
+
/** 当前文本块 ID(稳定 key) */
|
|
92
|
+
textBlockId: string | null;
|
|
98
93
|
}
|
|
99
94
|
/** 创建初始流式解析状态 */
|
|
100
95
|
declare function createStreamParseState(): StreamParseState;
|
|
@@ -133,7 +128,28 @@ declare function getLanguageDisplayName(language?: string): string;
|
|
|
133
128
|
/**
|
|
134
129
|
* Markdown 渲染工具
|
|
135
130
|
* 提供统一的 Markdown 渲染函数,供 React 和 Vue 版本使用
|
|
131
|
+
* 支持 LaTeX 数学公式渲染和 Mermaid 图表
|
|
132
|
+
*/
|
|
133
|
+
/**
|
|
134
|
+
* Mermaid 源码编码成可安全放入 attribute 的 base64url(UTF-8)
|
|
135
|
+
* 给前端组件(非 markdown 渲染路径)复用,避免重复实现。
|
|
136
|
+
*/
|
|
137
|
+
declare function encodeMermaidCodeToBase64Url(code: string): string;
|
|
138
|
+
/**
|
|
139
|
+
* 初始化 Mermaid(仅需调用一次)
|
|
140
|
+
*/
|
|
141
|
+
declare function initMermaid(): void;
|
|
142
|
+
/**
|
|
143
|
+
* 渲染页面中的 Mermaid 图表
|
|
144
|
+
* 用户点击"显示图表"按钮后调用
|
|
145
|
+
* @param container 容器元素(可选,默认为 document)
|
|
146
|
+
*/
|
|
147
|
+
declare function renderMermaidDiagrams(container?: Element): Promise<void>;
|
|
148
|
+
/**
|
|
149
|
+
* 渲染 fenced 的 LaTeX 代码块(```latex / ```katex),用于“先代码、后渲染”的视图切换。
|
|
150
|
+
* 注意:这里只返回 HTML 字符串,容器样式由 `.latex-block` 控制。
|
|
136
151
|
*/
|
|
152
|
+
declare function renderLatexBlockToHtml(code: string): string;
|
|
137
153
|
/**
|
|
138
154
|
* 渲染 Markdown 为 HTML
|
|
139
155
|
* @param markdown Markdown 文本
|
|
@@ -141,4 +157,18 @@ declare function getLanguageDisplayName(language?: string): string;
|
|
|
141
157
|
*/
|
|
142
158
|
declare function renderMarkdown(markdown: string): string;
|
|
143
159
|
|
|
144
|
-
|
|
160
|
+
declare function isMermaidLanguage(lang?: string): boolean;
|
|
161
|
+
declare function isLatexLanguage(lang?: string): boolean;
|
|
162
|
+
declare function isLatexDocument(code: string): boolean;
|
|
163
|
+
declare function canVisualizeLatex(code: string): boolean;
|
|
164
|
+
declare function hasLatexDelimiters(code: string): boolean;
|
|
165
|
+
declare function shouldShowVisualToggle(lang?: string, code?: string): boolean;
|
|
166
|
+
/**
|
|
167
|
+
* 将 fenced 的 latex/katex/tex 代码块渲染成 HTML(复用 shared 的 renderMarkdown + latex 预处理)。
|
|
168
|
+
* - 如果是完整 LaTeX 文档:返回提示 + code block(避免 KaTeX 报错)
|
|
169
|
+
* - 如果已经带分隔符:直接渲染(避免二次包裹)
|
|
170
|
+
* - 否则:自动包一层 $$...$$ 作为 display 公式渲染
|
|
171
|
+
*/
|
|
172
|
+
declare function renderFencedLatexToHtml(code: string): string;
|
|
173
|
+
|
|
174
|
+
export { type CodeBlock, type CodeExecutionResult, type ContentBlock, type ContentBlockBase, type ContentBlockType, type FileOperationResult, type SearchResultItem, type StreamParseState, type TextBlock, type WeatherData, type WeatherForecastItem, canVisualizeLatex, createStreamParseState, encodeMermaidCodeToBase64Url, finishStreamParse, getLanguageDisplayName, hasLatexDelimiters, highlightCode, initMermaid, isLatexDocument, isLatexLanguage, isMermaidLanguage, parseContent, parseContentStream, renderFencedLatexToHtml, renderLatexBlockToHtml, renderMarkdown, renderMermaidDiagrams, shouldShowVisualToggle };
|
package/dist/index.js
CHANGED
|
@@ -1,262 +1 @@
|
|
|
1
|
-
|
|
2
|
-
function generateId() {
|
|
3
|
-
return Math.random().toString(36).slice(2, 11);
|
|
4
|
-
}
|
|
5
|
-
var CODE_BLOCK_REGEX = /```(\w*)\n([\s\S]*?)```/g;
|
|
6
|
-
function parseContent(raw) {
|
|
7
|
-
if (!raw) return [];
|
|
8
|
-
const blocks = [];
|
|
9
|
-
let lastIndex = 0;
|
|
10
|
-
let match;
|
|
11
|
-
const regex = new RegExp(CODE_BLOCK_REGEX.source, "g");
|
|
12
|
-
while ((match = regex.exec(raw)) !== null) {
|
|
13
|
-
if (match.index > lastIndex) {
|
|
14
|
-
const textContent = raw.slice(lastIndex, match.index);
|
|
15
|
-
if (textContent.trim()) {
|
|
16
|
-
blocks.push(createTextBlock(textContent));
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
const language = match[1] || void 0;
|
|
20
|
-
const code = match[2];
|
|
21
|
-
blocks.push(createCodeBlock(code, language));
|
|
22
|
-
lastIndex = match.index + match[0].length;
|
|
23
|
-
}
|
|
24
|
-
if (lastIndex < raw.length) {
|
|
25
|
-
const textContent = raw.slice(lastIndex);
|
|
26
|
-
if (textContent.trim()) {
|
|
27
|
-
blocks.push(createTextBlock(textContent));
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return blocks;
|
|
31
|
-
}
|
|
32
|
-
function createTextBlock(content) {
|
|
33
|
-
return {
|
|
34
|
-
id: generateId(),
|
|
35
|
-
type: "text",
|
|
36
|
-
content: content.trim()
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
function createCodeBlock(content, language, filename) {
|
|
40
|
-
return {
|
|
41
|
-
id: generateId(),
|
|
42
|
-
type: "code",
|
|
43
|
-
content,
|
|
44
|
-
language,
|
|
45
|
-
filename
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
function createStreamParseState() {
|
|
49
|
-
return {
|
|
50
|
-
buffer: "",
|
|
51
|
-
blocks: [],
|
|
52
|
-
inCodeBlock: false,
|
|
53
|
-
codeLanguage: void 0,
|
|
54
|
-
codeContent: ""
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
function parseContentStream(chunk, state) {
|
|
58
|
-
const newState = { ...state };
|
|
59
|
-
newState.buffer += chunk;
|
|
60
|
-
while (true) {
|
|
61
|
-
if (newState.inCodeBlock) {
|
|
62
|
-
const endIndex = newState.buffer.indexOf("```");
|
|
63
|
-
if (endIndex !== -1) {
|
|
64
|
-
newState.codeContent += newState.buffer.slice(0, endIndex);
|
|
65
|
-
newState.blocks.push(createCodeBlock(
|
|
66
|
-
newState.codeContent.trim(),
|
|
67
|
-
newState.codeLanguage
|
|
68
|
-
));
|
|
69
|
-
newState.buffer = newState.buffer.slice(endIndex + 3);
|
|
70
|
-
newState.inCodeBlock = false;
|
|
71
|
-
newState.codeLanguage = void 0;
|
|
72
|
-
newState.codeContent = "";
|
|
73
|
-
} else {
|
|
74
|
-
newState.codeContent += newState.buffer;
|
|
75
|
-
newState.buffer = "";
|
|
76
|
-
break;
|
|
77
|
-
}
|
|
78
|
-
} else {
|
|
79
|
-
const startIndex = newState.buffer.indexOf("```");
|
|
80
|
-
if (startIndex !== -1) {
|
|
81
|
-
const beforeCode = newState.buffer.slice(0, startIndex);
|
|
82
|
-
if (beforeCode.trim()) {
|
|
83
|
-
newState.blocks.push(createTextBlock(beforeCode));
|
|
84
|
-
}
|
|
85
|
-
const afterStart = newState.buffer.slice(startIndex + 3);
|
|
86
|
-
const newlineIndex = afterStart.indexOf("\n");
|
|
87
|
-
if (newlineIndex !== -1) {
|
|
88
|
-
newState.codeLanguage = afterStart.slice(0, newlineIndex).trim() || void 0;
|
|
89
|
-
newState.buffer = afterStart.slice(newlineIndex + 1);
|
|
90
|
-
newState.inCodeBlock = true;
|
|
91
|
-
newState.codeContent = "";
|
|
92
|
-
} else {
|
|
93
|
-
break;
|
|
94
|
-
}
|
|
95
|
-
} else {
|
|
96
|
-
const lastBackticks = newState.buffer.lastIndexOf("`");
|
|
97
|
-
if (lastBackticks !== -1 && newState.buffer.length - lastBackticks < 3) {
|
|
98
|
-
const safeText = newState.buffer.slice(0, lastBackticks);
|
|
99
|
-
if (safeText.trim()) {
|
|
100
|
-
newState.blocks.push(createTextBlock(safeText));
|
|
101
|
-
}
|
|
102
|
-
newState.buffer = newState.buffer.slice(lastBackticks);
|
|
103
|
-
} else {
|
|
104
|
-
if (newState.buffer.trim()) {
|
|
105
|
-
newState.blocks.push(createTextBlock(newState.buffer));
|
|
106
|
-
}
|
|
107
|
-
newState.buffer = "";
|
|
108
|
-
}
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
return newState;
|
|
114
|
-
}
|
|
115
|
-
function finishStreamParse(state) {
|
|
116
|
-
const blocks = [...state.blocks];
|
|
117
|
-
if (state.inCodeBlock) {
|
|
118
|
-
blocks.push(createCodeBlock(
|
|
119
|
-
(state.codeContent + state.buffer).trim(),
|
|
120
|
-
state.codeLanguage
|
|
121
|
-
));
|
|
122
|
-
} else if (state.buffer.trim()) {
|
|
123
|
-
blocks.push(createTextBlock(state.buffer));
|
|
124
|
-
}
|
|
125
|
-
return blocks;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// src/highlighter.ts
|
|
129
|
-
import hljs from "highlight.js";
|
|
130
|
-
var LANGUAGE_ALIASES = {
|
|
131
|
-
js: "javascript",
|
|
132
|
-
ts: "typescript",
|
|
133
|
-
tsx: "typescript",
|
|
134
|
-
jsx: "javascript",
|
|
135
|
-
py: "python",
|
|
136
|
-
rb: "ruby",
|
|
137
|
-
sh: "bash",
|
|
138
|
-
shell: "bash",
|
|
139
|
-
yml: "yaml",
|
|
140
|
-
md: "markdown"
|
|
141
|
-
};
|
|
142
|
-
function highlightCode(code, language) {
|
|
143
|
-
if (!language) {
|
|
144
|
-
try {
|
|
145
|
-
const result = hljs.highlightAuto(code);
|
|
146
|
-
return result.value;
|
|
147
|
-
} catch {
|
|
148
|
-
return escapeHtml(code);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
const normalizedLang = LANGUAGE_ALIASES[language.toLowerCase()] || language.toLowerCase();
|
|
152
|
-
try {
|
|
153
|
-
if (hljs.getLanguage(normalizedLang)) {
|
|
154
|
-
return hljs.highlight(code, { language: normalizedLang }).value;
|
|
155
|
-
}
|
|
156
|
-
return escapeHtml(code);
|
|
157
|
-
} catch {
|
|
158
|
-
return escapeHtml(code);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
function escapeHtml(text) {
|
|
162
|
-
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
163
|
-
}
|
|
164
|
-
function getLanguageDisplayName(language) {
|
|
165
|
-
if (!language) return "Plain Text";
|
|
166
|
-
const normalizedLang = LANGUAGE_ALIASES[language.toLowerCase()] || language.toLowerCase();
|
|
167
|
-
const langDef = hljs.getLanguage(normalizedLang);
|
|
168
|
-
if (langDef) {
|
|
169
|
-
return normalizedLang.charAt(0).toUpperCase() + normalizedLang.slice(1);
|
|
170
|
-
}
|
|
171
|
-
return language;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// src/markdown.ts
|
|
175
|
-
import { marked, Renderer } from "marked";
|
|
176
|
-
import DOMPurify from "dompurify";
|
|
177
|
-
function renderMarkdown(markdown) {
|
|
178
|
-
if (!markdown) return "";
|
|
179
|
-
const renderer = new Renderer();
|
|
180
|
-
renderer.code = (code, language) => {
|
|
181
|
-
const highlighted = highlightCode(code, language);
|
|
182
|
-
const lang = language || "plaintext";
|
|
183
|
-
return `<pre class="markdown-code-block"><code class="language-${lang}">${highlighted}</code></pre>`;
|
|
184
|
-
};
|
|
185
|
-
renderer.table = (header, body) => {
|
|
186
|
-
return `<div class="markdown-table-wrapper"><table class="markdown-table"><thead>${header}</thead><tbody>${body}</tbody></table></div>`;
|
|
187
|
-
};
|
|
188
|
-
renderer.link = (href, title, text) => {
|
|
189
|
-
const titleAttr = title ? ` title="${title}"` : "";
|
|
190
|
-
return `<a href="${href}"${titleAttr} target="_blank" rel="noopener noreferrer" class="markdown-link">${text}</a>`;
|
|
191
|
-
};
|
|
192
|
-
renderer.listitem = (text, task, checked) => {
|
|
193
|
-
if (task) {
|
|
194
|
-
const checkboxText = checked ? "[x]" : "[ ]";
|
|
195
|
-
const textWithoutCheckbox = text.replace(/<input[^>]*>/g, "").trim();
|
|
196
|
-
return `<li class="markdown-task-item"><span class="markdown-task-checkbox">${checkboxText}</span> ${textWithoutCheckbox}</li>`;
|
|
197
|
-
}
|
|
198
|
-
return `<li>${text}</li>`;
|
|
199
|
-
};
|
|
200
|
-
marked.setOptions({
|
|
201
|
-
breaks: true,
|
|
202
|
-
// 支持换行
|
|
203
|
-
gfm: true
|
|
204
|
-
// GitHub Flavored Markdown
|
|
205
|
-
});
|
|
206
|
-
let html = marked(markdown, { renderer });
|
|
207
|
-
html = DOMPurify.sanitize(html, {
|
|
208
|
-
ALLOWED_TAGS: [
|
|
209
|
-
"p",
|
|
210
|
-
"br",
|
|
211
|
-
"strong",
|
|
212
|
-
"em",
|
|
213
|
-
"u",
|
|
214
|
-
"s",
|
|
215
|
-
"code",
|
|
216
|
-
"pre",
|
|
217
|
-
"h1",
|
|
218
|
-
"h2",
|
|
219
|
-
"h3",
|
|
220
|
-
"h4",
|
|
221
|
-
"h5",
|
|
222
|
-
"h6",
|
|
223
|
-
"ul",
|
|
224
|
-
"ol",
|
|
225
|
-
"li",
|
|
226
|
-
"blockquote",
|
|
227
|
-
"table",
|
|
228
|
-
"thead",
|
|
229
|
-
"tbody",
|
|
230
|
-
"tr",
|
|
231
|
-
"th",
|
|
232
|
-
"td",
|
|
233
|
-
"a",
|
|
234
|
-
"img",
|
|
235
|
-
"hr",
|
|
236
|
-
"div",
|
|
237
|
-
"span"
|
|
238
|
-
],
|
|
239
|
-
ALLOWED_ATTR: [
|
|
240
|
-
"href",
|
|
241
|
-
"title",
|
|
242
|
-
"target",
|
|
243
|
-
"rel",
|
|
244
|
-
"class",
|
|
245
|
-
"src",
|
|
246
|
-
"alt",
|
|
247
|
-
"width",
|
|
248
|
-
"height"
|
|
249
|
-
]
|
|
250
|
-
});
|
|
251
|
-
return html;
|
|
252
|
-
}
|
|
253
|
-
export {
|
|
254
|
-
createStreamParseState,
|
|
255
|
-
finishStreamParse,
|
|
256
|
-
getLanguageDisplayName,
|
|
257
|
-
highlightCode,
|
|
258
|
-
parseContent,
|
|
259
|
-
parseContentStream,
|
|
260
|
-
renderMarkdown
|
|
261
|
-
};
|
|
262
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
function e(){return Math.random().toString(36).slice(2,11)}var t=/```(\w*)\n([\s\S]*?)```/g;function r(e){if(!e)return[];const r=[];let c,a=0;const i=new RegExp(t.source,"g");for(;null!==(c=i.exec(e));){if(c.index>a){const t=e.slice(a,c.index);t.trim()&&r.push(n(t,void 0,!0))}const t=c[1]||void 0,i=c[2];r.push(o(i,t)),a=c.index+c[0].length}if(a<e.length){const t=e.slice(a);t.trim()&&r.push(n(t,void 0,!0))}return r}function n(t,r,n=!0){return{id:r||e(),type:"text",content:n?t.trim():t}}function o(t,r,n,o){return{id:o||e(),type:"code",content:t,language:r,filename:n}}function c(){return{buffer:"",blocks:[],inCodeBlock:!1,codeLanguage:void 0,codeContent:"",codeBlockId:null,textContent:"",textBlockId:null}}function a(t,r){r&&(null===t.textBlockId&&(t.textBlockId=e()),t.textContent+=r)}function i(e){e.textContent.trim()&&(e.blocks.push(n(e.textContent,e.textBlockId||void 0,!1)),e.textContent="",e.textBlockId=null)}function s(t,r){const n={...r,blocks:[...r.blocks]};for(n.buffer+=t;;)if(n.inCodeBlock){const e=n.buffer.indexOf("```");if(-1===e){n.codeContent+=n.buffer,n.buffer="";break}n.codeContent+=n.buffer.slice(0,e),n.blocks.push(o(n.codeContent.trim(),n.codeLanguage,void 0,n.codeBlockId||void 0)),n.buffer=n.buffer.slice(e+3),n.inCodeBlock=!1,n.codeLanguage=void 0,n.codeContent="",n.codeBlockId=null}else{const t=n.buffer.indexOf("```");if(-1===t){const e=n.buffer.lastIndexOf("`");if(-1!==e&&n.buffer.length-e<3){const t=n.buffer.slice(0,e);a(n,t),n.buffer=n.buffer.slice(e)}else a(n,n.buffer),n.buffer="";break}{const r=n.buffer.slice(0,t);a(n,r),i(n);const o=n.buffer.slice(t+3),c=o.indexOf("\n");if(-1===c)break;n.codeLanguage=o.slice(0,c).trim()||void 0,n.buffer=o.slice(c+1),n.inCodeBlock=!0,n.codeContent="",n.codeBlockId=e()}}return n}function l(e){const t=[...e.blocks];if(e.inCodeBlock)t.push(o((e.codeContent+e.buffer).trim(),e.codeLanguage,void 0,e.codeBlockId||void 0));else{const r=e.textContent+e.buffer;r.trim()&&t.push(n(r,e.textBlockId||void 0,!1))}return t}import d from"highlight.js";var u={js:"javascript",ts:"typescript",tsx:"typescript",jsx:"javascript",py:"python",rb:"ruby",sh:"bash",shell:"bash",yml:"yaml",md:"markdown"};function m(e,t){if(!t)try{return d.highlightAuto(e).value}catch{return f(e)}const r=u[t.toLowerCase()]||t.toLowerCase();try{return d.getLanguage(r)?d.highlight(e,{language:r}).value:f(e)}catch{return f(e)}}function f(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function p(e){if(!e)return"Plain Text";const t=u[e.toLowerCase()]||e.toLowerCase();return d.getLanguage(t)?t.charAt(0).toUpperCase()+t.slice(1):e}import{marked as h,Renderer as g}from"marked";import b from"dompurify";import x from"katex";import $ from"mermaid";var k=!1;function v(e){const t=(new TextEncoder).encode(e);let r="";for(const e of t)r+=String.fromCharCode(e);return btoa(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/g,"")}function L(e){return v(e)}function y(e){let t=e.replace(/\s/g,"+").replace(/-/g,"+").replace(/_/g,"/");const r=t.length%4;2===r?t+="==":3===r&&(t+="=");const n=atob(t),o=new Uint8Array(n.length);for(let e=0;e<n.length;e++)o[e]=n.charCodeAt(e);return(new TextDecoder).decode(o)}function w(){k||($.initialize({startOnLoad:!1,theme:"dark",securityLevel:"loose",fontFamily:"inherit",flowchart:{useMaxWidth:!0,htmlLabels:!0,curve:"basis"},sequence:{useMaxWidth:!0},gantt:{useMaxWidth:!0}}),k=!0)}async function C(e){k||w();const t=(e||document).querySelectorAll(".mermaid-placeholder:not(.mermaid-rendered):not(.mermaid-error-container)");for(const e of t){const t=e.getAttribute("data-mermaid-code-b64");if(!t)continue;let r="";try{r=y(t)}catch(t){e.innerHTML='<div class="mermaid-error">图表解码失败</div>',e.classList.remove("mermaid-placeholder"),e.classList.add("mermaid-error-container");continue}e.innerHTML='<div class="mermaid-loading">渲染中...</div>';try{const t=`mermaid-${Math.random().toString(36).slice(2,11)}`,{svg:n}=await $.render(t,r);e.innerHTML=n,e.classList.remove("mermaid-placeholder"),e.classList.add("mermaid-rendered"),e.removeAttribute("data-mermaid-code-b64")}catch(t){e.innerHTML=`<div class="mermaid-error">图表渲染失败</div><pre class="mermaid-source">${r.replace(/</g,"<").replace(/>/g,">")}</pre>`,e.classList.remove("mermaid-placeholder"),e.classList.add("mermaid-error-container")}}}function B(e,t){try{return x.renderToString(e,{displayMode:t,throwOnError:!1,strict:!1,trust:!0,output:"html"})}catch(r){return t?`<div class="latex-error">$$${e}$$</div>`:`<span class="latex-error">$${e}$</span>`}}function A(e){return`<div class="latex-block chat-scrollbar">${B(e.trim(),!0)}</div>`}function T(e){if(!e)return"";const{processed:t,placeholders:r}=function(e){const t=new Map;let r=0;const n=()=>`\x3c!--LATEX:${r++}--\x3e`;let o=e;return o=o.replace(/\$\$([\s\S]*?)\$\$/g,(e,r)=>{const o=n(),c=B(r.trim(),!0);return t.set(o,`<div class="latex-block chat-scrollbar">${c}</div>`),o}),o=o.replace(/\\\[([\s\S]*?)\\\]/g,(e,r)=>{const o=n(),c=B(r.trim(),!0);return t.set(o,`<div class="latex-block chat-scrollbar">${c}</div>`),o}),o=o.replace(/(?<!\$)\$(?!\$)((?:\\.|[^$\n])+?)\$(?!\$)/g,(e,r)=>{const o=n(),c=B(r.trim(),!1);return t.set(o,`<span class="latex-inline">${c}</span>`),o}),o=o.replace(/\\\(([\s\S]*?)\\\)/g,(e,r)=>{const o=n(),c=B(r.trim(),!1);return t.set(o,`<span class="latex-inline">${c}</span>`),o}),{processed:o,placeholders:t}}(e),n=new g;n.code=(e,t)=>{const r=(t||"plaintext").toLowerCase();if("mermaid"===r){return`<div class="mermaid-placeholder" data-mermaid-code-b64="${v(e)}"><div class="mermaid-loading">加载图表中...</div></div>`}return`<pre class="markdown-code-block chat-scrollbar"><code class="language-${r}">${m(e,t)}</code></pre>`},n.table=(e,t)=>`<div class="markdown-table-wrapper chat-scrollbar"><table class="markdown-table"><thead>${e}</thead><tbody>${t}</tbody></table></div>`,n.link=(e,t,r)=>`<a href="${e}"${t?` title="${t}"`:""} target="_blank" rel="noopener noreferrer" class="markdown-link">${r}</a>`,n.listitem=(e,t,r)=>{if(t){return`<li class="markdown-task-item"><span class="markdown-task-checkbox">${r?"[x]":"[ ]"}</span> ${e.replace(/<input[^>]*>/g,"").trim()}</li>`}return`<li>${e}</li>`},h.setOptions({breaks:!0,gfm:!0});let o=h(t,{renderer:n});return o=function(e,t){let r=e;return t.forEach((e,t)=>{r=r.replace(t,e)}),r}(o,r),o=b.sanitize(o,{ALLOWED_TAGS:["p","br","strong","em","u","s","code","pre","h1","h2","h3","h4","h5","h6","ul","ol","li","blockquote","table","thead","tbody","tr","th","td","a","img","hr","div","span","math","semantics","mrow","mi","mn","mo","msup","msub","mfrac","mroot","msqrt","mtext","mspace","mtable","mtr","mtd","annotation","mover","munder","munderover","menclose","mpadded","svg","line","path","rect","circle","g","use","defs","symbol"],ALLOWED_ATTR:["href","title","target","rel","class","src","alt","width","height","data-mermaid-code-b64","style","xmlns","viewBox","preserveAspectRatio","d","x","y","x1","y1","x2","y2","r","cx","cy","fill","stroke","stroke-width","transform","xlink:href","aria-hidden","focusable","role","mathvariant","encoding","stretchy","fence","separator","lspace","rspace","minsize","maxsize","accent","accentunder"],ADD_ATTR:["xmlns:xlink"]}),o}function S(e){return(e||"").trim().toLowerCase()}function I(e){return"mermaid"===S(e)}function M(e){const t=S(e);return"latex"===t||"katex"===t||"tex"===t}function O(e){const t=(e||"").trim();return/\\documentclass\b|\\usepackage\b|\\begin\{document\}|\\end\{document\}/.test(t)}function E(e){const t=(e||"").trim();return!!t&&!O(t)}function _(e){const t=(e||"").trim();return!!t&&(/\$\$[\s\S]*\$\$/.test(t)||/\\\[[\s\S]*\\\]/.test(t)||/\\\([\s\S]*\\\)/.test(t)||/(?<!\$)\$(?!\$)[\s\S]*?\$(?!\$)/.test(t))}function j(e,t){return!!I(e)||!!M(e)&&E(t||"")}function q(e){const t=(e||"").trim();return t?E(t)?_(t)?T(t):T(`$$\n${t}\n$$`):T(`> 不支持将完整 LaTeX 文档作为公式渲染,请切换到代码视图查看。\n\n\`\`\`latex\n${t}\n\`\`\``):""}export{E as canVisualizeLatex,c as createStreamParseState,L as encodeMermaidCodeToBase64Url,l as finishStreamParse,p as getLanguageDisplayName,_ as hasLatexDelimiters,m as highlightCode,w as initMermaid,O as isLatexDocument,M as isLatexLanguage,I as isMermaidLanguage,r as parseContent,s as parseContentStream,q as renderFencedLatexToHtml,A as renderLatexBlockToHtml,T as renderMarkdown,C as renderMermaidDiagrams,j as shouldShowVisualToggle};
|