Skip to content

Commit 5e30624

Browse files
committed
fix:实现stop按钮
1 parent d05ff5b commit 5e30624

File tree

2 files changed

+101
-44
lines changed

2 files changed

+101
-44
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515

1616
除了直接通过指令操作,
1717
你也可以使用图形化的UI:
18+
19+
1820
![图形化界面演示](/images/guide/readme-guide.png)
1921

22+
2023
## 使用方法
2124

2225
#### 使用前需要先设置 DeepSeek API 的 Key

src/chatPanel.ts

Lines changed: 98 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ export class ChatPanel {
178178
<div id="input-container">
179179
<textarea id="input" placeholder="Type your message here... (Ctrl+Enter to send)"></textarea>
180180
<button id="send">Send</button>
181+
<button id="stop" style="display: none;">Stop</button>
181182
<button id="new-session" style="position: absolute; top: 10px; right: 10px;">New Session</button>
182183
</div>
183184
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
@@ -186,59 +187,90 @@ export class ChatPanel {
186187
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/contrib/auto-render.min.js"></script>
187188
<script>
188189
const vscode = acquireVsCodeApi();
190+
189191
const chat = document.getElementById('chat');
190192
const input = document.getElementById('input');
193+
const sendButton = document.getElementById('send');
194+
const stopButton = document.getElementById('stop');
191195
192196
// 初始化代码高亮
193197
hljs.configure({ ignoreUnescapedHTML: true });
194198
195199
// 消息处理
196200
window.addEventListener('message', (event) => {
197-
const { role, content } = event.data;
198-
const lastChild = chat.lastElementChild;
199-
200-
let targetDiv;
201-
if (lastChild && lastChild.classList.contains(role)) {
202-
targetDiv = lastChild;
203-
targetDiv.dataset.markdownContent += content;
204-
} else {
205-
targetDiv = document.createElement('div');
206-
targetDiv.className = role;
207-
targetDiv.dataset.markdownContent = content;
208-
chat.appendChild(targetDiv);
209-
}
201+
const data = event.data;
202+
203+
// 处理 role 和 content 的消息
204+
if (data.role && data.content) {
205+
const { role, content } = data;
206+
const lastChild = chat.lastElementChild;
207+
208+
let targetDiv;
209+
if (lastChild && lastChild.classList.contains(role)) {
210+
targetDiv = lastChild;
211+
targetDiv.dataset.markdownContent += content;
212+
} else {
213+
targetDiv = document.createElement('div');
214+
targetDiv.className = role;
215+
targetDiv.dataset.markdownContent = content;
216+
chat.appendChild(targetDiv);
217+
}
218+
219+
if (role === 'model') {
220+
// 解析 Markdown
221+
targetDiv.innerHTML = marked.parse(targetDiv.dataset.markdownContent, {
222+
breaks: true,
223+
mangle: false,
224+
headerIds: false,
225+
highlight: (code, lang) => {
226+
const validLang = hljs.getLanguage(lang) ? lang : 'plaintext';
227+
return hljs.highlight(code, { language: validLang }).value;
228+
}
229+
});
230+
231+
// 渲染数学公式
232+
renderMathInElement(targetDiv, {
233+
delimiters: [
234+
{ left: '$$', right: '$$', display: true },
235+
{ left: '$', right: '$', display: false },
236+
{ left: '\\[', right: '\\]', display: true },
237+
{ left: '\\(', right: '\\)', display: false }
238+
],
239+
throwOnError: false
240+
});
241+
242+
// 重新高亮代码块
243+
hljs.highlightAll();
244+
} else {
245+
// 用户消息保持纯文本
246+
targetDiv.textContent = targetDiv.dataset.markdownContent;
247+
}
210248
211-
if (role === 'model') {
212-
// 解析Markdown
213-
targetDiv.innerHTML = marked.parse(targetDiv.dataset.markdownContent, {
214-
breaks: true,
215-
mangle: false,
216-
headerIds: false,
217-
highlight: (code, lang) => {
218-
const validLang = hljs.getLanguage(lang) ? lang : 'plaintext';
219-
return hljs.highlight(code, { language: validLang }).value;
220-
}
221-
});
222-
223-
// 渲染数学公式
224-
renderMathInElement(targetDiv, {
225-
delimiters: [
226-
{ left: '$$', right: '$$', display: true },
227-
{ left: '$', right: '$', display: false },
228-
{ left: '\\[', right: '\\]', display: true },
229-
{ left: '\\(', right: '\\)', display: false }
230-
],
231-
throwOnError: false
232-
});
233-
234-
// 重新高亮代码块
235-
hljs.highlightAll();
236-
} else {
237-
// 用户消息保持纯文本
238-
targetDiv.textContent = targetDiv.dataset.markdownContent;
249+
chat.scrollTop = chat.scrollHeight;
239250
}
251+
252+
// 处理 command 类型的消息
253+
if (data.command) {
254+
const { command } = data;
240255
241-
chat.scrollTop = chat.scrollHeight;
256+
switch (command) {
257+
case 'disableSendButton':
258+
sendButton.disabled = true;
259+
break;
260+
case 'enableSendButton':
261+
sendButton.disabled = false;
262+
break;
263+
case 'showStopButton':
264+
stopButton.style.display = 'inline-block';
265+
break;
266+
case 'hideStopButton':
267+
stopButton.style.display = 'none';
268+
break;
269+
default:
270+
// 处理其他 command 消息
271+
break;
272+
}
273+
}
242274
});
243275
244276
// 发送消息逻辑
@@ -250,6 +282,12 @@ export class ChatPanel {
250282
document.getElementById('new-session').addEventListener('click', () => {
251283
vscode.postMessage({ command: 'newSession' });
252284
chat.innerHTML = '';
285+
sendButton.disabled = false;
286+
stopButton.style.display = 'none';
287+
});
288+
289+
stopButton.addEventListener('click', () => {
290+
vscode.postMessage({ command: 'stop' });
253291
});
254292
255293
input.addEventListener('keydown', (e) => {
@@ -266,13 +304,17 @@ export class ChatPanel {
266304

267305
private async _handleMessage(message: any) {
268306
const webviewOutputChannel = new WebviewOutputChannel(this._panel.webview, 'DeepSeek API Output');
269-
307+
270308
switch (message.command) {
271309
case 'sendMessage':
272310
this._conversation.push({ role: 'user', content: message.text });
273311
this._panel.webview.postMessage({ role: 'user', content: message.text });
274312

275313
try {
314+
// 发送消息到 Webview,禁用发送按钮并显示停止按钮
315+
this._panel.webview.postMessage({ command: 'disableSendButton' });
316+
this._panel.webview.postMessage({ command: 'showStopButton' });
317+
276318
const response = await callDeepSeekApi(
277319
message.text,
278320
'You are a helpful assistant. Always format answers with Markdown.',
@@ -281,9 +323,17 @@ export class ChatPanel {
281323
undefined,
282324
getCurrentOperationController().signal
283325
);
284-
326+
327+
// 发送消息到 Webview,启用发送按钮并隐藏停止按钮
328+
this._panel.webview.postMessage({ command: 'enableSendButton' });
329+
this._panel.webview.postMessage({ command: 'hideStopButton' });
330+
285331
this._conversation.push({ role: 'model', content: response || '' });
286332
} catch (error) {
333+
// 发送消息到 Webview,启用发送按钮并隐藏停止按钮
334+
this._panel.webview.postMessage({ command: 'enableSendButton' });
335+
this._panel.webview.postMessage({ command: 'hideStopButton' });
336+
287337
this._panel.webview.postMessage({
288338
role: 'model',
289339
content: `**Error**: ${error instanceof Error ? error.message : 'Unknown error'}`
@@ -295,6 +345,10 @@ export class ChatPanel {
295345
this._conversation = [];
296346
resetCurrentOperationController();
297347
break;
348+
case 'stop':
349+
resetCurrentOperationController();
350+
this._panel.webview.postMessage({ role: 'model', content: '\n\n**Operation stopped by user**' });
351+
break;
298352
}
299353
}
300354

0 commit comments

Comments
 (0)