Skip to content

Commit 6c99d58

Browse files
committed
fix: 改进聊天的markdown显示
1 parent 0356271 commit 6c99d58

File tree

1 file changed

+128
-49
lines changed

1 file changed

+128
-49
lines changed

src/chatPanel.ts

Lines changed: 128 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -102,87 +102,152 @@ export class ChatPanel {
102102
<head>
103103
<meta charset="UTF-8">
104104
<meta name="viewport" content="width=device-width, initial-scale=1.0">
105+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src vscode-resource: https:; script-src vscode-resource: 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; style-src vscode-resource: 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com; font-src https://cdn.jsdelivr.net;">
105106
<title>Chat with Model</title>
107+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/atom-one-dark.min.css">
108+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css">
106109
<style>
110+
#chat {
111+
height: calc(100vh - 150px);
112+
overflow-y: auto;
113+
padding: 8px;
114+
}
107115
.user {
108-
color: black; /* 黑色字体 */
109-
background-color: #a3a3a3; /* 浅灰色背景 */
116+
background-color: #a3a3a3;
117+
color: black;
118+
padding: 12px;
119+
margin: 8px 0;
120+
border-radius: 4px;
121+
white-space: pre-wrap;
110122
}
111123
.model {
112-
color: white; /* 白色字体 */
124+
background-color: #333;
125+
color: white;
126+
padding: 12px;
127+
margin: 8px 0;
128+
border-radius: 4px;
113129
}
114-
#chat {
115-
height: calc(100vh - 150px);
116-
overflow-y: auto;
130+
.model pre code {
131+
background-color: #444 !important;
132+
padding: 1em;
133+
border-radius: 4px;
134+
display: block;
135+
overflow-x: auto;
136+
}
137+
.model code {
138+
background-color: #444;
139+
padding: 2px 4px;
140+
border-radius: 3px;
141+
}
142+
.katex {
143+
color: white !important;
144+
background-color: transparent !important;
117145
}
118146
#input-container {
119147
position: fixed;
120148
bottom: 0;
121149
width: 100%;
122-
background-color: white;
150+
background-color: var(--vscode-editor-background);
123151
padding: 10px;
152+
box-sizing: border-box;
124153
}
125-
#chat div {
126-
white-space: pre-wrap; /* 保留换行符 */
127-
margin-bottom: 8px; /* 段落间距(可选) */
154+
#input {
155+
width: 100%;
156+
height: 100px;
157+
padding: 8px;
158+
margin-bottom: 8px;
159+
color: var(--vscode-input-foreground);
160+
background-color: var(--vscode-input-background);
161+
border: 1px solid var(--vscode-input-border);
162+
}
163+
button {
164+
padding: 8px 16px;
165+
background-color: var(--vscode-button-background);
166+
color: var(--vscode-button-foreground);
167+
border: none;
168+
cursor: pointer;
128169
}
129170
</style>
130171
</head>
131172
<body>
132173
<div id="chat"></div>
133174
<div id="input-container">
134-
<textarea id="input" placeholder="Type your message here..." style="width: 100%; height: 100px;"></textarea>
175+
<textarea id="input" placeholder="Type your message here... (Ctrl+Enter to send)"></textarea>
135176
<button id="send">Send</button>
136177
<button id="reset">Reset</button>
137178
</div>
179+
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
180+
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
181+
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js"></script>
182+
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/contrib/auto-render.min.js"></script>
138183
<script>
139184
const vscode = acquireVsCodeApi();
140185
const chat = document.getElementById('chat');
141186
const input = document.getElementById('input');
142-
const send = document.getElementById('send');
143-
const reset = document.getElementById('reset');
144-
145-
input.addEventListener('keydown', (event) => {
146-
if (event.key === 'Enter' && event.ctrlKey) {
147-
vscode.postMessage({
148-
command: 'sendMessage',
149-
text: input.value
187+
188+
// 初始化代码高亮
189+
hljs.configure({ ignoreUnescapedHTML: true });
190+
191+
// 消息处理
192+
window.addEventListener('message', (event) => {
193+
const { role, content } = event.data;
194+
const lastChild = chat.lastElementChild;
195+
196+
let targetDiv;
197+
if (lastChild && lastChild.classList.contains(role)) {
198+
targetDiv = lastChild;
199+
targetDiv.dataset.markdownContent += content;
200+
} else {
201+
targetDiv = document.createElement('div');
202+
targetDiv.className = role;
203+
targetDiv.dataset.markdownContent = content;
204+
chat.appendChild(targetDiv);
205+
}
206+
207+
if (role === 'model') {
208+
// 解析Markdown
209+
targetDiv.innerHTML = marked.parse(targetDiv.dataset.markdownContent, {
210+
breaks: true,
211+
highlight: (code, lang) => {
212+
const validLang = hljs.getLanguage(lang) ? lang : 'plaintext';
213+
return hljs.highlight(code, { language: validLang }).value;
214+
}
150215
});
151-
input.value = '';
216+
217+
// 渲染数学公式
218+
renderMathInElement(targetDiv, {
219+
delimiters: [
220+
{ left: '$$', right: '$$', display: true },
221+
{ left: '$', right: '$', display: false }
222+
],
223+
throwOnError: false
224+
});
225+
226+
// 重新高亮代码块
227+
hljs.highlightAll();
228+
} else {
229+
// 用户消息保持纯文本
230+
targetDiv.textContent = targetDiv.dataset.markdownContent;
152231
}
232+
233+
chat.scrollTop = chat.scrollHeight;
153234
});
154235
155-
send.addEventListener('click', () => {
156-
vscode.postMessage({
157-
command: 'sendMessage',
158-
text: input.value
159-
});
236+
// 发送消息逻辑
237+
document.getElementById('send').addEventListener('click', () => {
238+
vscode.postMessage({ command: 'sendMessage', text: input.value });
160239
input.value = '';
161240
});
162241
163-
reset.addEventListener('click', () => {
164-
vscode.postMessage({
165-
command: 'reset'
166-
});
242+
document.getElementById('reset').addEventListener('click', () => {
243+
vscode.postMessage({ command: 'reset' });
167244
});
168245
169-
window.addEventListener('message', (event) => {
170-
const { role, content } = event.data;
171-
const chat = document.getElementById('chat');
172-
const lastChild = chat.lastElementChild;
173-
174-
// 合并到同一角色元素
175-
if (lastChild && lastChild.className === role) {
176-
lastChild.textContent += content;
177-
} else {
178-
const div = document.createElement('div');
179-
div.className = role;
180-
div.textContent = content;
181-
chat.appendChild(div);
246+
input.addEventListener('keydown', (e) => {
247+
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
248+
vscode.postMessage({ command: 'sendMessage', text: input.value });
249+
input.value = '';
182250
}
183-
184-
// 自动滚动到底部
185-
chat.scrollTop = chat.scrollHeight;
186251
});
187252
</script>
188253
</body>
@@ -197,13 +262,27 @@ export class ChatPanel {
197262
case 'sendMessage':
198263
this._conversation.push({ role: 'user', content: message.text });
199264
this._panel.webview.postMessage({ role: 'user', content: message.text });
200-
const response = await callDeepSeekApi(message.text, 'You are a helpful assistant.', webviewOutputChannel, true);
201-
this._conversation.push({ role: 'model', content: response || '' });
202-
this._panel.webview.postMessage({ role: 'model', content: response || '' });
265+
266+
try {
267+
const response = await callDeepSeekApi(
268+
message.text,
269+
'You are a helpful assistant. Always format answers with Markdown.',
270+
webviewOutputChannel,
271+
true
272+
);
273+
274+
this._conversation.push({ role: 'model', content: response || '' });
275+
} catch (error) {
276+
this._panel.webview.postMessage({
277+
role: 'model',
278+
content: `**Error**: ${error instanceof Error ? error.message : 'Unknown error'}`
279+
});
280+
}
203281
break;
282+
204283
case 'reset':
205284
this._conversation = [];
206-
this._panel.webview.html = this._getHtmlForWebview();
285+
this._panel.webview.postMessage({ command: 'clearHistory' });
207286
break;
208287
}
209288
}

0 commit comments

Comments
 (0)