fex:尝试修复流式显示工具调用不穿插显示的问题
This commit is contained in:
@ -681,6 +681,74 @@ export function getWebviewContent(iconUri?: string): string {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 分段消息样式 */
|
||||
.segmented-message {
|
||||
padding: 0;
|
||||
}
|
||||
.message-segment {
|
||||
padding: 10px 14px;
|
||||
}
|
||||
.segment-text {
|
||||
line-height: 1.6;
|
||||
}
|
||||
.segment-tool {
|
||||
background: var(--vscode-textBlockQuote-background);
|
||||
border-radius: 6px;
|
||||
margin: 8px 0;
|
||||
padding: 10px 14px;
|
||||
}
|
||||
.tool-segment-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.tool-segment-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
.tool-segment-name {
|
||||
font-weight: 500;
|
||||
color: var(--vscode-foreground);
|
||||
}
|
||||
.tool-segment-result {
|
||||
margin-top: 6px;
|
||||
font-size: 12px;
|
||||
color: var(--vscode-descriptionForeground);
|
||||
padding-left: 22px;
|
||||
}
|
||||
.segment-tool.tool-success {
|
||||
border-left: 3px solid var(--vscode-charts-green);
|
||||
}
|
||||
.segment-tool.tool-error {
|
||||
border-left: 3px solid var(--vscode-charts-red);
|
||||
}
|
||||
.segment-tool.tool-running {
|
||||
border-left: 3px solid var(--vscode-charts-blue);
|
||||
}
|
||||
.segment-question {
|
||||
background: var(--vscode-textBlockQuote-background);
|
||||
border-radius: 6px;
|
||||
margin: 8px 0;
|
||||
padding: 12px 14px;
|
||||
border-left: 3px solid var(--vscode-charts-orange);
|
||||
}
|
||||
.question-segment .question-text {
|
||||
margin-bottom: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.question-segment .question-options {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
.question-opt {
|
||||
padding: 4px 10px;
|
||||
background: var(--vscode-button-secondaryBackground);
|
||||
color: var(--vscode-button-secondaryForeground);
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 状态栏样式 */
|
||||
.status-bar {
|
||||
display: flex;
|
||||
@ -842,12 +910,15 @@ export function getWebviewContent(iconUri?: string): string {
|
||||
</div>
|
||||
|
||||
<script>
|
||||
console.log('[WebView] 脚本开始执行');
|
||||
const vscode = acquireVsCodeApi();
|
||||
console.log('[WebView] vscode API 已获取');
|
||||
const messagesEl = document.getElementById('messages');
|
||||
const messageInput = document.getElementById('messageInput');
|
||||
const modeSelect = document.getElementById('modeSelect');
|
||||
const filePathInput = document.getElementById('filePathInput');
|
||||
const fileContentEl = document.getElementById('fileContent');
|
||||
console.log('[WebView] DOM 元素已获取, messagesEl:', !!messagesEl);
|
||||
const errorMessageEl = document.getElementById('errorMessage');
|
||||
const fileEditorSection = document.getElementById('fileEditorSection');
|
||||
const fileEditorTextarea = document.getElementById('fileEditorTextarea');
|
||||
@ -1147,6 +1218,10 @@ export function getWebviewContent(iconUri?: string): string {
|
||||
addMessage(message.text, 'bot');
|
||||
}
|
||||
break;
|
||||
case 'receiveSegments':
|
||||
// 渲染分段消息
|
||||
renderSegments(message.segments);
|
||||
break;
|
||||
case 'updateStreamingMessage':
|
||||
// 流式更新消息
|
||||
updateOrCreateStreamingMessage(message.text);
|
||||
@ -1209,7 +1284,7 @@ export function getWebviewContent(iconUri?: string): string {
|
||||
messageContent.textContent = text;
|
||||
div.appendChild(messageContent);
|
||||
|
||||
messagesContainer.appendChild(div);
|
||||
messagesEl.appendChild(div);
|
||||
currentStreamingMessage = div;
|
||||
} else {
|
||||
// 更新现有消息内容
|
||||
@ -1220,7 +1295,7 @@ export function getWebviewContent(iconUri?: string): string {
|
||||
}
|
||||
|
||||
// 滚动到底部
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
messagesEl.scrollTop = messagesEl.scrollHeight;
|
||||
}
|
||||
|
||||
// 完成流式消息
|
||||
@ -1246,7 +1321,7 @@ export function getWebviewContent(iconUri?: string): string {
|
||||
currentStreamingMessage = null;
|
||||
}
|
||||
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
messagesEl.scrollTop = messagesEl.scrollHeight;
|
||||
}
|
||||
|
||||
// 显示加载指示器
|
||||
@ -1261,8 +1336,8 @@ export function getWebviewContent(iconUri?: string): string {
|
||||
</div>
|
||||
<span class="loading-text">\${text}</span>
|
||||
\`;
|
||||
messagesContainer.appendChild(loadingIndicator);
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
messagesEl.appendChild(loadingIndicator);
|
||||
messagesEl.scrollTop = messagesEl.scrollHeight;
|
||||
}
|
||||
|
||||
// 隐藏加载指示器
|
||||
@ -1292,6 +1367,94 @@ export function getWebviewContent(iconUri?: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
// 渲染分段消息
|
||||
function renderSegments(segments) {
|
||||
console.log('[WebView] renderSegments 被调用, segments:', segments);
|
||||
if (!segments || segments.length === 0) {
|
||||
console.log('[WebView] segments 为空,跳过渲染');
|
||||
return;
|
||||
}
|
||||
|
||||
// 移除流式消息(如果有)
|
||||
if (currentStreamingMessage) {
|
||||
console.log('[WebView] 移除流式消息');
|
||||
currentStreamingMessage.remove();
|
||||
currentStreamingMessage = null;
|
||||
}
|
||||
|
||||
// 移除所有工具状态消息(因为会在分段中显示)
|
||||
const toolStatuses = messagesEl.querySelectorAll('.tool-status');
|
||||
console.log('[WebView] 找到工具状态消息数量:', toolStatuses.length);
|
||||
toolStatuses.forEach(el => {
|
||||
console.log('[WebView] 移除工具状态消息:', el.className);
|
||||
el.remove();
|
||||
});
|
||||
|
||||
// 创建消息容器
|
||||
const container = document.createElement('div');
|
||||
container.className = 'message bot-message segmented-message';
|
||||
|
||||
segments.forEach((segment, index) => {
|
||||
const segmentDiv = document.createElement('div');
|
||||
segmentDiv.className = 'message-segment segment-' + segment.type;
|
||||
|
||||
if (segment.type === 'text' && segment.content) {
|
||||
segmentDiv.innerHTML = formatText(segment.content);
|
||||
} else if (segment.type === 'tool') {
|
||||
const statusIcon = segment.toolStatus === 'success' ? '✅' :
|
||||
segment.toolStatus === 'error' ? '❌' : '🔧';
|
||||
const statusClass = 'tool-' + (segment.toolStatus || 'running');
|
||||
segmentDiv.className += ' ' + statusClass;
|
||||
segmentDiv.innerHTML = \`
|
||||
<div class="tool-segment-header">
|
||||
<span class="tool-segment-icon">\${statusIcon}</span>
|
||||
<span class="tool-segment-name">\${segment.toolName || '工具'}</span>
|
||||
</div>
|
||||
\${segment.toolResult ? \`<div class="tool-segment-result">\${segment.toolResult}</div>\` : ''}
|
||||
\`;
|
||||
} else if (segment.type === 'question') {
|
||||
segmentDiv.innerHTML = \`
|
||||
<div class="question-segment">
|
||||
<div class="question-text">\${segment.question || ''}</div>
|
||||
<div class="question-options">
|
||||
\${(segment.options || []).map(opt => \`<span class="question-opt">\${opt}</span>\`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
\`;
|
||||
}
|
||||
|
||||
container.appendChild(segmentDiv);
|
||||
});
|
||||
|
||||
// 添加操作按钮
|
||||
const actionsDiv = document.createElement('div');
|
||||
actionsDiv.className = 'message-actions';
|
||||
const copyBtn = document.createElement('button');
|
||||
copyBtn.className = 'action-btn';
|
||||
copyBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>';
|
||||
copyBtn.onclick = () => {
|
||||
const textContent = segments
|
||||
.filter(s => s.type === 'text' && s.content)
|
||||
.map(s => s.content)
|
||||
.join('\\n');
|
||||
copyMessage(textContent, copyBtn);
|
||||
};
|
||||
actionsDiv.appendChild(copyBtn);
|
||||
container.appendChild(actionsDiv);
|
||||
|
||||
messagesEl.appendChild(container);
|
||||
messagesEl.scrollTop = messagesEl.scrollHeight;
|
||||
}
|
||||
|
||||
// 格式化文本(简单的换行处理)
|
||||
function formatText(text) {
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/\\n/g, '<br>');
|
||||
}
|
||||
|
||||
// 添加工具状态消息
|
||||
function addToolStatus(toolName, status, detail) {
|
||||
const statusIcons = {
|
||||
@ -1313,8 +1476,8 @@ export function getWebviewContent(iconUri?: string): string {
|
||||
<span class="tool-status-text">\${statusTexts[status]}</span>
|
||||
\${detail ? \`<div class="tool-detail">\${detail}</div>\` : ''}
|
||||
\`;
|
||||
messagesContainer.appendChild(div);
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
messagesEl.appendChild(div);
|
||||
messagesEl.scrollTop = messagesEl.scrollHeight;
|
||||
}
|
||||
|
||||
// 显示用户问题
|
||||
@ -1357,8 +1520,8 @@ export function getWebviewContent(iconUri?: string): string {
|
||||
}
|
||||
};
|
||||
|
||||
messagesContainer.appendChild(div);
|
||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||
messagesEl.appendChild(div);
|
||||
messagesEl.scrollTop = messagesEl.scrollHeight;
|
||||
}
|
||||
|
||||
// 提交用户回答
|
||||
|
||||
Reference in New Issue
Block a user