Skip to content

Commit 4a3b0c2

Browse files
committed
fix:聊天流式输出
1 parent 43d9c72 commit 4a3b0c2

File tree

1 file changed

+79
-5
lines changed

1 file changed

+79
-5
lines changed

src/chatPanel.ts

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,63 @@
11
import * as vscode from 'vscode';
22
import { callDeepSeekApi } from './deepseekApi';
33

4+
class WebviewOutputChannel implements vscode.OutputChannel {
5+
private _webview: vscode.Webview;
6+
private _name: string;
7+
8+
constructor(webview: vscode.Webview, name: string) {
9+
this._webview = webview;
10+
this._name = name;
11+
}
12+
13+
get name(): string {
14+
return this._name;
15+
}
16+
17+
append(value: string): void {
18+
// 将数据通过 Webview 发送出去
19+
this._webview.postMessage({ role: 'model', content: value });
20+
}
21+
22+
appendLine(value: string): void {
23+
this.append(value + '\n');
24+
}
25+
26+
clear(): void {
27+
// 清除输出,通常可以通过清空 Webview 来实现
28+
this._webview.postMessage({ role: 'model', content: '' });
29+
}
30+
31+
show(preserveFocus?: boolean): void;
32+
show(column?: vscode.ViewColumn, preserveFocus?: boolean): void;
33+
show(arg1?: boolean | vscode.ViewColumn, arg2?: boolean): void {
34+
if (typeof arg1 === 'boolean') {
35+
// 第一种重载:show(preserveFocus?: boolean)
36+
this._webview.postMessage({ role: 'model', content: 'Webview is now shown' });
37+
} else {
38+
// 第二种重载:show(column?: ViewColumn, preserveFocus?: boolean)
39+
if (arg1 !== undefined) {
40+
// 根据 column 进行处理(可以自定义逻辑)
41+
console.log(`Showing in column: ${arg1}`);
42+
}
43+
this._webview.postMessage({ role: 'model', content: 'Webview is now shown' });
44+
}
45+
}
46+
hide(): void {
47+
48+
}
49+
50+
dispose(): void{
51+
52+
}
53+
54+
replace(value: string): void {
55+
// 替换输出内容
56+
this._webview.postMessage({ role: 'model', content: value });
57+
}
58+
}
59+
60+
461
export class ChatPanel {
562
private static readonly viewType = 'chatPanel';
663
private static currentPanel: ChatPanel | undefined;
@@ -60,6 +117,10 @@ export class ChatPanel {
60117
background-color: white;
61118
padding: 10px;
62119
}
120+
#chat div {
121+
white-space: pre-wrap; /* 关键代码:保留换行符 */
122+
margin-bottom: 8px; /* 段落间距(可选) */
123+
}
63124
</style>
64125
</head>
65126
<body>
@@ -102,10 +163,21 @@ export class ChatPanel {
102163
103164
window.addEventListener('message', (event) => {
104165
const { role, content } = event.data;
105-
const div = document.createElement('div');
106-
div.className = role;
107-
div.textContent = content;
108-
chat.appendChild(div);
166+
const chat = document.getElementById('chat');
167+
const lastChild = chat.lastElementChild;
168+
169+
// 合并到同一角色元素
170+
if (lastChild && lastChild.className === role) {
171+
lastChild.textContent += content;
172+
} else {
173+
const div = document.createElement('div');
174+
div.className = role;
175+
div.textContent = content;
176+
chat.appendChild(div);
177+
}
178+
179+
// 自动滚动到底部
180+
chat.scrollTop = chat.scrollHeight;
109181
});
110182
</script>
111183
</body>
@@ -114,11 +186,13 @@ export class ChatPanel {
114186
}
115187

116188
private async _handleMessage(message: any) {
189+
const webviewOutputChannel = new WebviewOutputChannel(this._panel.webview, 'DeepSeek API Output');
190+
117191
switch (message.command) {
118192
case 'sendMessage':
119193
this._conversation.push({ role: 'user', content: message.text });
120194
this._panel.webview.postMessage({ role: 'user', content: message.text });
121-
const response = await callDeepSeekApi(message.text, 'You are a helpful assistant.');
195+
const response = await callDeepSeekApi(message.text, 'You are a helpful assistant.', webviewOutputChannel, true);
122196
this._conversation.push({ role: 'model', content: response || '' });
123197
this._panel.webview.postMessage({ role: 'model', content: response || '' });
124198
break;

0 commit comments

Comments
 (0)