@@ -22,12 +22,12 @@ class WebviewOutputChannel implements vscode.OutputChannel {
2222
2323 append ( value : string ) : void {
2424 this . webview . postMessage ( { role : 'model' , content : value } ) ;
25- // getOutputChannel().append(value);
25+ getOutputChannel ( ) . append ( value ) ;
2626 }
2727
2828 appendLine ( value : string ) : void {
2929 this . append ( value + '\n' ) ;
30- // getOutputChannel().appendLine (value);
30+ getOutputChannel ( ) . append ( value + '\n' ) ;
3131 }
3232
3333 clear ( ) : void {
@@ -196,6 +196,26 @@ export class ChatPanel {
196196 border: none;
197197 cursor: pointer;
198198 }
199+ #mermaid-toggle-container {
200+ position: absolute;
201+ top: 10px;
202+ right: 80px;
203+ }
204+ #mermaid-toggle {
205+ cursor: pointer;
206+ }
207+ .mermaid {
208+ margin: 10px 0;
209+ }
210+ .mermaid-raw {
211+ display: none;
212+ }
213+ .mermaid-rendered .mermaid-raw {
214+ display: block;
215+ }
216+ .mermaid-rendered .mermaid {
217+ display: none;
218+ }
199219 </style>
200220 </head>
201221 <body>
@@ -205,12 +225,16 @@ export class ChatPanel {
205225 <button id="send">Send</button>
206226 <button id="stop" style="display: none;">Stop</button>
207227 <button id="new-session" style="position: absolute; top: 10px; right: 10px;">New Session</button>
208-
228+ <div id="mermaid-toggle-container">
229+ <input type="checkbox" id="mermaid-toggle">
230+ <label for="mermaid-toggle">Show Mermaid Raw Code</label>
231+ </div>
209232 <input type="checkbox" id="web-search">
210233 <label for="web-search">联网搜索</label>
211234 </div>
212235 <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
213236 <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
237+ <script src="https://cdn.jsdelivr.net/npm/[email protected] /dist/mermaid.min.js"></script> 214238 <script defer src="https://cdn.jsdelivr.net/npm/[email protected] /dist/katex.min.js"></script> 215239 <script defer src="https://cdn.jsdelivr.net/npm/[email protected] /dist/contrib/auto-render.min.js"></script> 216240 <script>
@@ -221,6 +245,13 @@ export class ChatPanel {
221245 const stopButton = document.getElementById('stop');
222246 const newSessionButton = document.getElementById('new-session');
223247 const webSearchCheckbox = document.getElementById('web-search');
248+ const mermaidToggle = document.getElementById('mermaid-toggle');
249+
250+ // 初始化 Mermaid
251+ mermaid.initialize({
252+ startOnLoad: false,
253+ theme: 'dark'
254+ });
224255
225256 // 配置代码高亮
226257 hljs.configure({ ignoreUnescapedHTML: true });
@@ -292,44 +323,68 @@ export class ChatPanel {
292323 * 把 container.innerHTML 里所有的 $$…$$ 块,
293324 * 用 katex.renderToString 直接渲染成 HTML
294325 */
295- function fnRenderDisplayMath(webviewDiv)
296- {
297- // 1) 获取原始 HTML
326+ function fnRenderDisplayMath(webviewDiv) {
298327 const strRawHtml = webviewDiv.innerHTML;
299-
300- // 2) 匹配所有 $$…$$(非贪婪)
301328 const rgxDisplayMath = /\$\$([\s\S]+?)\$\$/g;
302-
303- // 3) 替换成 katex 渲染结果
304- const strReplacedHtml = strRawHtml.replace(rgxDisplayMath
305- ,
306- (strMatch, strInnerTex) =>
307- {
308- try
309- {
310- // trim 首尾空白,保持 display 模式
329+ const strReplacedHtml = strRawHtml.replace(rgxDisplayMath, (strMatch, strInnerTex) => {
330+ try {
311331 const strTex = strInnerTex.replace(/^\s+|\s+$/g, '');
312- return katex.renderToString(strTex
313- ,
314- {
332+ return katex.renderToString(strTex, {
315333 displayMode: true,
316334 throwOnError: false
317335 });
318- }
319- catch (err)
320- {
336+ } catch (err) {
321337 console.error('KaTeX render error:', err);
322- // 渲染失败就返回原始 $$…$$
323338 return strMatch;
324339 }
325340 });
326-
327- // 4) 更新回 DOM
328341 webviewDiv.innerHTML = strReplacedHtml;
329342 }
330343
344+ // 渲染 Mermaid 图表
345+ async function renderMermaid(webviewDiv) {
346+ const codeBlocks = webviewDiv.querySelectorAll('pre code.language-mermaid');
347+ for (const codeBlock of codeBlocks) {
348+ const parentPre = codeBlock.closest('pre');
349+ const mermaidCode = codeBlock.textContent;
350+ try {
351+ const { svg } = await mermaid.render('mermaid-diagram-' + Date.now(), mermaidCode);
352+ const mermaidDiv = document.createElement('div');
353+ mermaidDiv.className = 'mermaid';
354+ mermaidDiv.innerHTML = svg;
355+
356+ const rawDiv = document.createElement('div');
357+ rawDiv.className = 'mermaid-raw';
358+ rawDiv.innerHTML = \`<pre><code class="language-mermaid">\${mermaidCode}</code></pre>\`;
359+
360+ const container = document.createElement('div');
361+ container.className = 'mermaid-container';
362+ container.appendChild(mermaidDiv);
363+ container.appendChild(rawDiv);
364+
365+ parentPre.replaceWith(container);
366+ } catch (err) {
367+ console.error('Mermaid render error:', err);
368+ const errorDiv = document.createElement('div');
369+ errorDiv.className = 'mermaid-error';
370+ errorDiv.textContent = 'Failed to render Mermaid diagram';
371+ parentPre.replaceWith(errorDiv);
372+ }
373+ }
374+ }
375+
376+ // 切换 Mermaid 显示模式
377+ function toggleMermaidDisplay() {
378+ const containers = document.querySelectorAll('.mermaid-container');
379+ if (mermaidToggle.checked) {
380+ containers.forEach(container => container.classList.add('mermaid-rendered'));
381+ } else {
382+ containers.forEach(container => container.classList.remove('mermaid-rendered'));
383+ }
384+ }
385+
331386 // 渲染消息内容
332- function renderMessage(role, content, index) {
387+ async function renderMessage(role, content, index) {
333388 const lastChild = chat.lastElementChild;
334389 let targetDiv;
335390
@@ -352,6 +407,7 @@ export class ChatPanel {
352407 });
353408
354409 fnRenderDisplayMath(targetDiv);
410+ await renderMermaid(targetDiv);
355411
356412 renderMathInElement(targetDiv, {
357413 delimiters: [
@@ -373,11 +429,11 @@ export class ChatPanel {
373429 }
374430
375431 // 处理 Webview 消息
376- window.addEventListener('message', (event) => {
432+ window.addEventListener('message', async (event) => {
377433 const data = event.data;
378434
379435 if (data.role && data.content) {
380- renderMessage(data.role, data.content, data.index);
436+ await renderMessage(data.role, data.content, data.index);
381437 return;
382438 }
383439
@@ -411,6 +467,7 @@ export class ChatPanel {
411467
412468 // 初始化事件监听
413469 setupCopyButtonDelegation();
470+ mermaidToggle.addEventListener('change', toggleMermaidDisplay);
414471
415472 // 发送消息
416473 sendButton.addEventListener('click', () => {
@@ -503,7 +560,7 @@ export class ChatPanel {
503560
504561 try {
505562 const tools = message . webSearch ? [ apiTools . searchTool ] : null ;
506- const nomalSystemPromot = "用markdown输出。" ;
563+ const nomalSystemPromot = "用markdown输出。数学公式要用$$包裹,每条一行不要换行。流程图(Mermaid)里的每个字符串都要用引号包裹。 " ;
507564 const systemPrompt = message . webSearch ? "每次回答问题前,一定要先上网搜索一下再回答。" + nomalSystemPromot : nomalSystemPromot ;
508565 const response = await callDeepSeekApi (
509566 this . conversation . map ( msg => ( { role : msg . role , content : msg . content } ) ) ,
0 commit comments