@huyooo/ai-chat-shared 0.2.14 → 0.2.16

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.js CHANGED
@@ -1,561 +1 @@
1
- // src/parser.ts
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, void 0, true));
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, void 0, true));
28
- }
29
- }
30
- return blocks;
31
- }
32
- function createTextBlock(content, id, trimContent = true) {
33
- return {
34
- id: id || generateId(),
35
- type: "text",
36
- content: trimContent ? content.trim() : content
37
- };
38
- }
39
- function createCodeBlock(content, language, filename, id) {
40
- return {
41
- id: 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
- codeBlockId: null,
56
- textContent: "",
57
- textBlockId: null
58
- };
59
- }
60
- function appendText(state, text) {
61
- if (!text) return;
62
- if (state.textBlockId === null) state.textBlockId = generateId();
63
- state.textContent += text;
64
- }
65
- function flushTextBlock(state) {
66
- if (!state.textContent.trim()) return;
67
- state.blocks.push(createTextBlock(state.textContent, state.textBlockId || void 0, false));
68
- state.textContent = "";
69
- state.textBlockId = null;
70
- }
71
- function parseContentStream(chunk, state) {
72
- const newState = { ...state, blocks: [...state.blocks] };
73
- newState.buffer += chunk;
74
- while (true) {
75
- if (newState.inCodeBlock) {
76
- const endIndex = newState.buffer.indexOf("```");
77
- if (endIndex !== -1) {
78
- newState.codeContent += newState.buffer.slice(0, endIndex);
79
- newState.blocks.push(
80
- createCodeBlock(
81
- newState.codeContent.trim(),
82
- newState.codeLanguage,
83
- void 0,
84
- newState.codeBlockId || void 0
85
- )
86
- );
87
- newState.buffer = newState.buffer.slice(endIndex + 3);
88
- newState.inCodeBlock = false;
89
- newState.codeLanguage = void 0;
90
- newState.codeContent = "";
91
- newState.codeBlockId = null;
92
- } else {
93
- newState.codeContent += newState.buffer;
94
- newState.buffer = "";
95
- break;
96
- }
97
- } else {
98
- const startIndex = newState.buffer.indexOf("```");
99
- if (startIndex !== -1) {
100
- const beforeCode = newState.buffer.slice(0, startIndex);
101
- appendText(newState, beforeCode);
102
- flushTextBlock(newState);
103
- const afterStart = newState.buffer.slice(startIndex + 3);
104
- const newlineIndex = afterStart.indexOf("\n");
105
- if (newlineIndex !== -1) {
106
- newState.codeLanguage = afterStart.slice(0, newlineIndex).trim() || void 0;
107
- newState.buffer = afterStart.slice(newlineIndex + 1);
108
- newState.inCodeBlock = true;
109
- newState.codeContent = "";
110
- newState.codeBlockId = generateId();
111
- } else {
112
- break;
113
- }
114
- } else {
115
- const lastBackticks = newState.buffer.lastIndexOf("`");
116
- if (lastBackticks !== -1 && newState.buffer.length - lastBackticks < 3) {
117
- const safeText = newState.buffer.slice(0, lastBackticks);
118
- appendText(newState, safeText);
119
- newState.buffer = newState.buffer.slice(lastBackticks);
120
- } else {
121
- appendText(newState, newState.buffer);
122
- newState.buffer = "";
123
- }
124
- break;
125
- }
126
- }
127
- }
128
- return newState;
129
- }
130
- function finishStreamParse(state) {
131
- const blocks = [...state.blocks];
132
- if (state.inCodeBlock) {
133
- blocks.push(
134
- createCodeBlock(
135
- (state.codeContent + state.buffer).trim(),
136
- state.codeLanguage,
137
- void 0,
138
- state.codeBlockId || void 0
139
- )
140
- );
141
- } else {
142
- const merged = state.textContent + state.buffer;
143
- if (merged.trim()) {
144
- blocks.push(createTextBlock(merged, state.textBlockId || void 0, false));
145
- }
146
- }
147
- return blocks;
148
- }
149
-
150
- // src/highlighter.ts
151
- import hljs from "highlight.js";
152
- var LANGUAGE_ALIASES = {
153
- js: "javascript",
154
- ts: "typescript",
155
- tsx: "typescript",
156
- jsx: "javascript",
157
- py: "python",
158
- rb: "ruby",
159
- sh: "bash",
160
- shell: "bash",
161
- yml: "yaml",
162
- md: "markdown"
163
- };
164
- function highlightCode(code, language) {
165
- if (!language) {
166
- try {
167
- const result = hljs.highlightAuto(code);
168
- return result.value;
169
- } catch {
170
- return escapeHtml(code);
171
- }
172
- }
173
- const normalizedLang = LANGUAGE_ALIASES[language.toLowerCase()] || language.toLowerCase();
174
- try {
175
- if (hljs.getLanguage(normalizedLang)) {
176
- return hljs.highlight(code, { language: normalizedLang }).value;
177
- }
178
- return escapeHtml(code);
179
- } catch {
180
- return escapeHtml(code);
181
- }
182
- }
183
- function escapeHtml(text) {
184
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
185
- }
186
- function getLanguageDisplayName(language) {
187
- if (!language) return "Plain Text";
188
- const normalizedLang = LANGUAGE_ALIASES[language.toLowerCase()] || language.toLowerCase();
189
- const langDef = hljs.getLanguage(normalizedLang);
190
- if (langDef) {
191
- return normalizedLang.charAt(0).toUpperCase() + normalizedLang.slice(1);
192
- }
193
- return language;
194
- }
195
-
196
- // src/markdown.ts
197
- import { marked, Renderer } from "marked";
198
- import DOMPurify from "dompurify";
199
- import katex from "katex";
200
- import mermaid from "mermaid";
201
- var mermaidInitialized = false;
202
- function encodeBase64Utf8(text) {
203
- const bytes = new TextEncoder().encode(text);
204
- let binary = "";
205
- for (const b of bytes) binary += String.fromCharCode(b);
206
- const b64 = btoa(binary);
207
- return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
208
- }
209
- function encodeMermaidCodeToBase64Url(code) {
210
- return encodeBase64Utf8(code);
211
- }
212
- function decodeBase64Utf8(b64) {
213
- const normalized = b64.replace(/\s/g, "+");
214
- let base64 = normalized.replace(/-/g, "+").replace(/_/g, "/");
215
- const pad = base64.length % 4;
216
- if (pad === 2) base64 += "==";
217
- else if (pad === 3) base64 += "=";
218
- else if (pad !== 0) {
219
- }
220
- const binary = atob(base64);
221
- const bytes = new Uint8Array(binary.length);
222
- for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
223
- return new TextDecoder().decode(bytes);
224
- }
225
- function initMermaid() {
226
- if (mermaidInitialized) return;
227
- mermaid.initialize({
228
- startOnLoad: false,
229
- theme: "dark",
230
- securityLevel: "loose",
231
- fontFamily: "inherit",
232
- flowchart: {
233
- useMaxWidth: true,
234
- htmlLabels: true,
235
- curve: "basis"
236
- },
237
- sequence: {
238
- useMaxWidth: true
239
- },
240
- gantt: {
241
- useMaxWidth: true
242
- }
243
- });
244
- mermaidInitialized = true;
245
- }
246
- async function renderMermaidDiagrams(container) {
247
- if (!mermaidInitialized) {
248
- initMermaid();
249
- }
250
- const root = container || document;
251
- const elements = root.querySelectorAll(".mermaid-placeholder:not(.mermaid-rendered):not(.mermaid-error-container)");
252
- for (const el of elements) {
253
- const b64 = el.getAttribute("data-mermaid-code-b64");
254
- if (!b64) continue;
255
- let code = "";
256
- try {
257
- code = decodeBase64Utf8(b64);
258
- } catch (e) {
259
- console.warn("Mermaid \u89E3\u7801\u5931\u8D25:", e);
260
- el.innerHTML = `<div class="mermaid-error">\u56FE\u8868\u89E3\u7801\u5931\u8D25</div>`;
261
- el.classList.remove("mermaid-placeholder");
262
- el.classList.add("mermaid-error-container");
263
- continue;
264
- }
265
- el.innerHTML = `<div class="mermaid-loading">\u6E32\u67D3\u4E2D...</div>`;
266
- try {
267
- const id = `mermaid-${Math.random().toString(36).slice(2, 11)}`;
268
- const { svg } = await mermaid.render(id, code);
269
- el.innerHTML = svg;
270
- el.classList.remove("mermaid-placeholder");
271
- el.classList.add("mermaid-rendered");
272
- el.removeAttribute("data-mermaid-code-b64");
273
- } catch (error) {
274
- console.warn("Mermaid \u6E32\u67D3\u5931\u8D25:", error);
275
- el.innerHTML = `<div class="mermaid-error">\u56FE\u8868\u6E32\u67D3\u5931\u8D25</div><pre class="mermaid-source">${code.replace(/</g, "&lt;").replace(/>/g, "&gt;")}</pre>`;
276
- el.classList.remove("mermaid-placeholder");
277
- el.classList.add("mermaid-error-container");
278
- }
279
- }
280
- }
281
- function renderLatex(latex, displayMode) {
282
- try {
283
- return katex.renderToString(latex, {
284
- displayMode,
285
- throwOnError: false,
286
- strict: false,
287
- trust: true,
288
- output: "html"
289
- });
290
- } catch (error) {
291
- console.warn("LaTeX \u6E32\u67D3\u5931\u8D25:", error);
292
- return displayMode ? `<div class="latex-error">$$${latex}$$</div>` : `<span class="latex-error">$${latex}$</span>`;
293
- }
294
- }
295
- function renderLatexBlockToHtml(code) {
296
- return `<div class="latex-block chat-scrollbar">${renderLatex(code.trim(), true)}</div>`;
297
- }
298
- function preprocessLatex(text) {
299
- const placeholders = /* @__PURE__ */ new Map();
300
- let counter = 0;
301
- const createPlaceholder = () => {
302
- const placeholder = `<!--LATEX:${counter++}-->`;
303
- return placeholder;
304
- };
305
- let processed = text;
306
- processed = processed.replace(/\$\$([\s\S]*?)\$\$/g, (_, latex) => {
307
- const placeholder = createPlaceholder();
308
- const rendered = renderLatex(latex.trim(), true);
309
- placeholders.set(placeholder, `<div class="latex-block chat-scrollbar">${rendered}</div>`);
310
- return placeholder;
311
- });
312
- processed = processed.replace(/\\\[([\s\S]*?)\\\]/g, (_, latex) => {
313
- const placeholder = createPlaceholder();
314
- const rendered = renderLatex(latex.trim(), true);
315
- placeholders.set(placeholder, `<div class="latex-block chat-scrollbar">${rendered}</div>`);
316
- return placeholder;
317
- });
318
- processed = processed.replace(/(?<!\$)\$(?!\$)((?:\\.|[^$\n])+?)\$(?!\$)/g, (_, latex) => {
319
- const placeholder = createPlaceholder();
320
- const rendered = renderLatex(latex.trim(), false);
321
- placeholders.set(placeholder, `<span class="latex-inline">${rendered}</span>`);
322
- return placeholder;
323
- });
324
- processed = processed.replace(/\\\(([\s\S]*?)\\\)/g, (_, latex) => {
325
- const placeholder = createPlaceholder();
326
- const rendered = renderLatex(latex.trim(), false);
327
- placeholders.set(placeholder, `<span class="latex-inline">${rendered}</span>`);
328
- return placeholder;
329
- });
330
- return { processed, placeholders };
331
- }
332
- function postprocessLatex(html, placeholders) {
333
- let result = html;
334
- placeholders.forEach((rendered, placeholder) => {
335
- result = result.replace(placeholder, rendered);
336
- });
337
- return result;
338
- }
339
- function renderMarkdown(markdown) {
340
- if (!markdown) return "";
341
- const { processed, placeholders } = preprocessLatex(markdown);
342
- const renderer = new Renderer();
343
- renderer.code = (code, language) => {
344
- const lang = (language || "plaintext").toLowerCase();
345
- if (lang === "mermaid") {
346
- const b64 = encodeBase64Utf8(code);
347
- return `<div class="mermaid-placeholder" data-mermaid-code-b64="${b64}"><div class="mermaid-loading">\u52A0\u8F7D\u56FE\u8868\u4E2D...</div></div>`;
348
- }
349
- const highlighted = highlightCode(code, language);
350
- return `<pre class="markdown-code-block chat-scrollbar"><code class="language-${lang}">${highlighted}</code></pre>`;
351
- };
352
- renderer.table = (header, body) => {
353
- return `<div class="markdown-table-wrapper chat-scrollbar"><table class="markdown-table"><thead>${header}</thead><tbody>${body}</tbody></table></div>`;
354
- };
355
- renderer.link = (href, title, text) => {
356
- const titleAttr = title ? ` title="${title}"` : "";
357
- return `<a href="${href}"${titleAttr} target="_blank" rel="noopener noreferrer" class="markdown-link">${text}</a>`;
358
- };
359
- renderer.listitem = (text, task, checked) => {
360
- if (task) {
361
- const checkboxText = checked ? "[x]" : "[ ]";
362
- const textWithoutCheckbox = text.replace(/<input[^>]*>/g, "").trim();
363
- return `<li class="markdown-task-item"><span class="markdown-task-checkbox">${checkboxText}</span> ${textWithoutCheckbox}</li>`;
364
- }
365
- return `<li>${text}</li>`;
366
- };
367
- marked.setOptions({
368
- breaks: true,
369
- // 支持换行
370
- gfm: true
371
- // GitHub Flavored Markdown
372
- });
373
- let html = marked(processed, { renderer });
374
- html = postprocessLatex(html, placeholders);
375
- html = DOMPurify.sanitize(html, {
376
- ALLOWED_TAGS: [
377
- "p",
378
- "br",
379
- "strong",
380
- "em",
381
- "u",
382
- "s",
383
- "code",
384
- "pre",
385
- "h1",
386
- "h2",
387
- "h3",
388
- "h4",
389
- "h5",
390
- "h6",
391
- "ul",
392
- "ol",
393
- "li",
394
- "blockquote",
395
- "table",
396
- "thead",
397
- "tbody",
398
- "tr",
399
- "th",
400
- "td",
401
- "a",
402
- "img",
403
- "hr",
404
- "div",
405
- "span",
406
- // KaTeX 相关标签
407
- "math",
408
- "semantics",
409
- "mrow",
410
- "mi",
411
- "mn",
412
- "mo",
413
- "msup",
414
- "msub",
415
- "mfrac",
416
- "mroot",
417
- "msqrt",
418
- "mtext",
419
- "mspace",
420
- "mtable",
421
- "mtr",
422
- "mtd",
423
- "annotation",
424
- "mover",
425
- "munder",
426
- "munderover",
427
- "menclose",
428
- "mpadded",
429
- "svg",
430
- "line",
431
- "path",
432
- "rect",
433
- "circle",
434
- "g",
435
- "use",
436
- "defs",
437
- "symbol"
438
- ],
439
- ALLOWED_ATTR: [
440
- "href",
441
- "title",
442
- "target",
443
- "rel",
444
- "class",
445
- "src",
446
- "alt",
447
- "width",
448
- "height",
449
- // Mermaid 相关属性
450
- "data-mermaid-code-b64",
451
- // KaTeX 相关属性
452
- "style",
453
- "xmlns",
454
- "viewBox",
455
- "preserveAspectRatio",
456
- "d",
457
- "x",
458
- "y",
459
- "x1",
460
- "y1",
461
- "x2",
462
- "y2",
463
- "r",
464
- "cx",
465
- "cy",
466
- "fill",
467
- "stroke",
468
- "stroke-width",
469
- "transform",
470
- "xlink:href",
471
- "aria-hidden",
472
- "focusable",
473
- "role",
474
- "mathvariant",
475
- "encoding",
476
- "stretchy",
477
- "fence",
478
- "separator",
479
- "lspace",
480
- "rspace",
481
- "minsize",
482
- "maxsize",
483
- "accent",
484
- "accentunder"
485
- ],
486
- ADD_ATTR: ["xmlns:xlink"]
487
- });
488
- return html;
489
- }
490
-
491
- // src/visual.ts
492
- function normalizeLanguage(lang) {
493
- return (lang || "").trim().toLowerCase();
494
- }
495
- function isMermaidLanguage(lang) {
496
- return normalizeLanguage(lang) === "mermaid";
497
- }
498
- function isLatexLanguage(lang) {
499
- const l = normalizeLanguage(lang);
500
- return l === "latex" || l === "katex" || l === "tex";
501
- }
502
- function isLatexDocument(code) {
503
- const t = (code || "").trim();
504
- return /\\documentclass\b|\\usepackage\b|\\begin\{document\}|\\end\{document\}/.test(t);
505
- }
506
- function canVisualizeLatex(code) {
507
- const t = (code || "").trim();
508
- if (!t) return false;
509
- if (isLatexDocument(t)) return false;
510
- return true;
511
- }
512
- function hasLatexDelimiters(code) {
513
- const t = (code || "").trim();
514
- if (!t) return false;
515
- return /\$\$[\s\S]*\$\$/.test(t) || /\\\[[\s\S]*\\\]/.test(t) || /\\\([\s\S]*\\\)/.test(t) || /(?<!\$)\$(?!\$)[\s\S]*?\$(?!\$)/.test(t);
516
- }
517
- function shouldShowVisualToggle(lang, code) {
518
- if (isMermaidLanguage(lang)) return true;
519
- if (isLatexLanguage(lang)) return canVisualizeLatex(code || "");
520
- return false;
521
- }
522
- function renderFencedLatexToHtml(code) {
523
- const t = (code || "").trim();
524
- if (!t) return "";
525
- if (!canVisualizeLatex(t)) {
526
- return renderMarkdown(
527
- `> \u4E0D\u652F\u6301\u5C06\u5B8C\u6574 LaTeX \u6587\u6863\u4F5C\u4E3A\u516C\u5F0F\u6E32\u67D3\uFF0C\u8BF7\u5207\u6362\u5230\u4EE3\u7801\u89C6\u56FE\u67E5\u770B\u3002
528
-
529
- \`\`\`latex
530
- ${t}
531
- \`\`\``
532
- );
533
- }
534
- if (hasLatexDelimiters(t)) {
535
- return renderMarkdown(t);
536
- }
537
- return renderMarkdown(`$$
538
- ${t}
539
- $$`);
540
- }
541
- export {
542
- canVisualizeLatex,
543
- createStreamParseState,
544
- encodeMermaidCodeToBase64Url,
545
- finishStreamParse,
546
- getLanguageDisplayName,
547
- hasLatexDelimiters,
548
- highlightCode,
549
- initMermaid,
550
- isLatexDocument,
551
- isLatexLanguage,
552
- isMermaidLanguage,
553
- parseContent,
554
- parseContentStream,
555
- renderFencedLatexToHtml,
556
- renderLatexBlockToHtml,
557
- renderMarkdown,
558
- renderMermaidDiagrams,
559
- shouldShowVisualToggle
560
- };
561
- //# 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,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")}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,"&lt;").replace(/>/g,"&gt;")}</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};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huyooo/ai-chat-shared",
3
- "version": "0.2.14",
3
+ "version": "0.2.16",
4
4
  "description": "AI Chat 共享模块 - 内容解析器、类型定义",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -16,8 +16,7 @@
16
16
  "./styles": "./dist/styles.css"
17
17
  },
18
18
  "files": [
19
- "dist",
20
- "src"
19
+ "dist"
21
20
  ],
22
21
  "scripts": {
23
22
  "build": "tsup",