diff --git a/src/constants/toolIcons.ts b/src/constants/toolIcons.ts
index dc4352b..4a61fcd 100644
--- a/src/constants/toolIcons.ts
+++ b/src/constants/toolIcons.ts
@@ -200,3 +200,8 @@ export const setting = ``;
+
+/**
+ * 任务完成的图标svg
+ */
+export const taskCompleteIconSvg = ``;
diff --git a/src/panels/helpers/conversationHelper.ts b/src/panels/helpers/conversationHelper.ts
index 6280f8a..2550f2f 100644
--- a/src/panels/helpers/conversationHelper.ts
+++ b/src/panels/helpers/conversationHelper.ts
@@ -209,6 +209,11 @@ export async function selectConversation(
});
}
+ // 发送任务完成消息(历史记录)
+ panel.webview.postMessage({
+ command: "taskCompleteHistory",
+ });
+
vscode.window.showInformationMessage(
`已加载会话: ${taskSession.meta.taskName}`,
);
diff --git a/src/views/messageRenderer.ts b/src/views/messageRenderer.ts
index 277b707..662b3f7 100644
--- a/src/views/messageRenderer.ts
+++ b/src/views/messageRenderer.ts
@@ -48,11 +48,10 @@ export function getMessageRendererScript(): string {
const div = document.createElement('div');
div.className = \`message \${sender}-message\`;
if (sender === 'bot') {
- const messageContent = document.createElement('div');
- messageContent.textContent = text;
- div.appendChild(messageContent);
const actionsDiv = document.createElement('div');
actionsDiv.className = 'message-actions';
+ const messageContent = document.createElement('span');
+ messageContent.textContent = text;
const copyBtn = document.createElement('button');
copyBtn.className = 'action-btn';
copyBtn.innerHTML = \`复制\`;
@@ -65,6 +64,7 @@ export function getMessageRendererScript(): string {
dislikeBtn.className = 'action-btn';
dislikeBtn.innerHTML = \`点踩\`;
dislikeBtn.onclick = () => toggleDislike(dislikeBtn);
+ actionsDiv.appendChild(messageContent);
actionsDiv.appendChild(copyBtn);
actionsDiv.appendChild(likeBtn);
actionsDiv.appendChild(dislikeBtn);
@@ -97,7 +97,12 @@ export function getMessageRendererScript(): string {
}
function copyMessage(text, button) {
- navigator.clipboard.writeText(text).then(() => {
+ // 从按钮的父消息元素中获取实际文本内容
+ const messageDiv = button.closest('.message');
+ const messageContent = messageDiv?.querySelector('.message-content, div:not(.message-actions)');
+ const textToCopy = messageContent ? messageContent.textContent : text;
+
+ navigator.clipboard.writeText(textToCopy).then(() => {
const originalHTML = button.innerHTML;
button.innerHTML = \`\`;
setTimeout(() => {
diff --git a/src/views/messageStyles.ts b/src/views/messageStyles.ts
index 2e75afa..39f28ca 100644
--- a/src/views/messageStyles.ts
+++ b/src/views/messageStyles.ts
@@ -38,12 +38,18 @@ export function getMessageAreaStyles(): string {
position: relative;
}
.message-actions {
- display: flex;
+ display: inline-flex;
gap: 8px;
- margin-top: 12px;
- margin-left: 10px;
+ margin-left: 12px;
opacity: 0.85;
transition: opacity 0.2s ease;
+ vertical-align: middle;
+ align-items: center;
+ }
+ .message-actions > span {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
}
.message-actions:hover {
opacity: 1;
diff --git a/src/views/segmentRenderer.ts b/src/views/segmentRenderer.ts
index 7a49689..171aeef 100644
--- a/src/views/segmentRenderer.ts
+++ b/src/views/segmentRenderer.ts
@@ -8,11 +8,7 @@
export function getSegmentRendererScript(): string {
return `
function updateSegmentsRealtime(segments, isComplete) {
- if (isComplete && (!segments || segments.length === 0)) {
- currentSegmentedMessage = null;
- return;
- }
- if (!segments || segments.length === 0) return;
+ if (!isComplete && (!segments || segments.length === 0)) return;
if (!currentSegmentedMessage) {
if (currentStreamingMessage) {
@@ -40,11 +36,13 @@ export function getSegmentRendererScript(): string {
});
}
- currentSegmentedMessage.innerHTML = '';
+ if (!isComplete) {
+ currentSegmentedMessage.innerHTML = '';
+ }
const mergedSegments = [];
let i = 0;
- while (i < segments.length) {
+ while (i < (segments?.length || 0)) {
const segment = segments[i];
if (segment.type === 'tool') {
let count = 1;
@@ -216,34 +214,10 @@ export function getSegmentRendererScript(): string {
});
if (isComplete) {
- console.log('[WebView] 对话完成,添加操作按钮');
- const actionsDiv = document.createElement('div');
- actionsDiv.className = 'message-actions';
- const copyBtn = document.createElement('button');
- copyBtn.className = 'action-btn';
- copyBtn.innerHTML = \`复制\`;
- copyBtn.onclick = () => {
- const textContent = segments
- .filter(s => s.type === 'text' && s.content)
- .map(s => s.content)
- .join('\\n');
- copyMessage(textContent, copyBtn);
- };
- const likeBtn = document.createElement('button');
- likeBtn.className = 'action-btn';
- likeBtn.innerHTML = \`点赞\`;
- likeBtn.onclick = () => toggleLike(likeBtn);
- const dislikeBtn = document.createElement('button');
- dislikeBtn.className = 'action-btn';
- dislikeBtn.innerHTML = \`点踩\`;
- dislikeBtn.onclick = () => toggleDislike(dislikeBtn);
- actionsDiv.appendChild(copyBtn);
- actionsDiv.appendChild(likeBtn);
- actionsDiv.appendChild(dislikeBtn);
- currentSegmentedMessage.appendChild(actionsDiv);
currentSegmentedMessage = null;
}
+
smartScrollToBottom();
}
@@ -264,10 +238,34 @@ export function getSegmentRendererScript(): string {
console.log('[WebView] 移除工具状态消息:', el.className);
el.remove();
});
- const container = document.createElement('div');
- container.className = 'message bot-message segmented-message';
- updateSegmentsRealtime(segments, true);
- messagesEl.appendChild(container);
+ updateSegmentsRealtime(segments, false);
+
+ // 历史消息渲染完成后添加操作按钮
+ if (currentSegmentedMessage) {
+ const actionsDiv = document.createElement('div');
+ actionsDiv.className = 'message-actions';
+ const copyBtn = document.createElement('button');
+ copyBtn.className = 'action-btn';
+ copyBtn.innerHTML = \`复制\`;
+ copyBtn.onclick = () => {
+ const textContent = segments.filter(s => s.type === 'text' && s.content).map(s => s.content).join('\\n');
+ copyMessage(textContent, copyBtn);
+ };
+ const likeBtn = document.createElement('button');
+ likeBtn.className = 'action-btn';
+ likeBtn.innerHTML = \`点赞\`;
+ likeBtn.onclick = () => toggleLike(likeBtn);
+ const dislikeBtn = document.createElement('button');
+ dislikeBtn.className = 'action-btn';
+ dislikeBtn.innerHTML = \`点踩\`;
+ dislikeBtn.onclick = () => toggleDislike(dislikeBtn);
+ actionsDiv.appendChild(copyBtn);
+ actionsDiv.appendChild(likeBtn);
+ actionsDiv.appendChild(dislikeBtn);
+ currentSegmentedMessage.appendChild(actionsDiv);
+ currentSegmentedMessage = null;
+ }
+
smartScrollToBottom();
}
`;
diff --git a/src/views/webviewContent.ts b/src/views/webviewContent.ts
index baddbc9..c31941b 100644
--- a/src/views/webviewContent.ts
+++ b/src/views/webviewContent.ts
@@ -25,6 +25,7 @@ import {
} from "./progressBar";
import { getHighlightJsLinks } from "../components/codeHighlight";
import { getCurrentEnv } from "../config/settings";
+import { taskCompleteIconSvg } from "../constants/toolIcons";
import {
getInvitationModalContent,
getInvitationModalStyles,
@@ -545,6 +546,9 @@ export function getWebviewContent(
const modeSelect = document.getElementById('modeSelect');
const messagesEl = document.getElementById('messages');
+ // 图标常量
+ const taskCompleteIconSvg = ${JSON.stringify(taskCompleteIconSvg)};
+
// 全局变量
let currentStreamingMessage = null;
let loadingIndicator = null;
@@ -769,10 +773,44 @@ export function getWebviewContent(
// 隐藏加载指示器
hideLoadingIndicator();
break;
-
case 'taskComplete':
// 显示任务完成提示
- addMessage('✅ 任务已完成', 'bot');
+ const taskDiv = document.createElement('div');
+ taskDiv.className = 'message bot-message';
+ const taskActionsDiv = document.createElement('div');
+ taskActionsDiv.className = 'message-actions';
+ const taskMessageContent = document.createElement('span');
+ taskMessageContent.innerHTML = taskCompleteIconSvg + ' 任务完成';
+ const taskCopyBtn = document.createElement('button');
+ taskCopyBtn.className = 'action-btn';
+ taskCopyBtn.innerHTML = \`复制\`;
+ taskCopyBtn.onclick = () => {
+ // 获取前一个 AI 消息的内容
+ const prevMessage = taskDiv.previousElementSibling;
+ if (prevMessage && prevMessage.classList.contains('bot-message')) {
+ const textContent = prevMessage.textContent || '';
+ copyMessage(textContent, taskCopyBtn);
+ }
+ };
+ const taskLikeBtn = document.createElement('button');
+ taskLikeBtn.className = 'action-btn';
+ taskLikeBtn.innerHTML = \`点赞\`;
+ taskLikeBtn.onclick = () => toggleLike(taskLikeBtn);
+ const taskDislikeBtn = document.createElement('button');
+ taskDislikeBtn.className = 'action-btn';
+ taskDislikeBtn.innerHTML = \`点踩\`;
+ taskDislikeBtn.onclick = () => toggleDislike(taskDislikeBtn);
+ taskActionsDiv.appendChild(taskMessageContent);
+ taskActionsDiv.appendChild(taskCopyBtn);
+ taskActionsDiv.appendChild(taskLikeBtn);
+ taskActionsDiv.appendChild(taskDislikeBtn);
+ taskDiv.appendChild(taskActionsDiv);
+ messagesEl.appendChild(taskDiv);
+ messagesEl.scrollTop = messagesEl.scrollHeight;
+ break;
+
+ case 'taskCompleteHistory':
+ // 历史记录不显示任务完成提示
break;
case 'workspaceStatus':