diff --git a/src/renderer/components/workspace-panel.ts b/src/renderer/components/workspace-panel.ts index 9fdb68b..10e6c27 100644 --- a/src/renderer/components/workspace-panel.ts +++ b/src/renderer/components/workspace-panel.ts @@ -833,7 +833,7 @@ function appendToolCardDOM(tc: ToolCallRecord): void { container.appendChild(card); // 等浏览器完成布局后再滚到底部 requestAnimationFrame(() => { - container.scrollTop = container.scrollHeight; + container.lastElementChild?.scrollIntoView({ block: 'end' }); }); } } @@ -938,9 +938,11 @@ function renderToolCalls(): void { html += renderToolCard(tc); } container.innerHTML = html; - // 等浏览器完成布局后再滚到底部 + // 全量重建后等两帧让布局稳定,再滚到底部 requestAnimationFrame(() => { - container.scrollTop = container.scrollHeight; + requestAnimationFrame(() => { + container.lastElementChild?.scrollIntoView({ block: 'end' }); + }); }); } diff --git a/src/renderer/services/agent-engine.ts b/src/renderer/services/agent-engine.ts index 8d55bbe..b5fced0 100644 --- a/src/renderer/services/agent-engine.ts +++ b/src/renderer/services/agent-engine.ts @@ -15,7 +15,7 @@ import { import { searchMemories, buildMemoryContext, markMemoryUsed, isMemoryEnabled } from './memory-manager.js'; import { extractSkillsFromToolRecords, matchSkills, buildSkillContext } from './skill-manager.js'; import { showToast } from '../components/toast.js'; -import { logInfo, logWarn, logSuccess, logError, logToolStart, logToolResult, logAgentLoop, logModelResponse, logStream } from './log-service.js'; +import { logInfo, logWarn, logSuccess, logError, logToolStart, logToolResult, logAgentLoop, logModelResponse, logStreamProgress } from './log-service.js'; import { getWorkspaceDirPath } from '../components/workspace-panel.js'; import { generateId } from '../utils/utils.js'; import { buildContext, estimateTokens, shouldAutoCompress, compressWithLLM, AUTO_COMPRESS_THRESHOLD, recordActualTokens } from './context-manager.js'; @@ -729,12 +729,12 @@ Shell: ${osInfo.shell} // 有新增内容 → 正常进度 lastLoggedLen = content.length; lastContentTime = Date.now(); - logStream(`流式输出中… ${content.length} 字 / ${sec}s${toolCalls.length > 0 ? ` [${toolCalls.length} 个工具调用]` : ''}`); + logStreamProgress(`流式输出中… ${content.length} 字 / ${sec}s${toolCalls.length > 0 ? ` [${toolCalls.length} 个工具调用]` : ''}`); } else if (content.length > 0) { // 内容不变 → 计算真正的无新内容时长 const idleSec = Math.round((Date.now() - lastContentTime) / 1000); if (idleSec >= 10) { - logStream(`⚠️ ${idleSec}s 无新内容,当前 ${content.length} 字`); + logStreamProgress(`⚠️ ${idleSec}s 无新内容,当前 ${content.length} 字`); } } }, PROGRESS_INTERVAL); diff --git a/src/renderer/services/log-service.ts b/src/renderer/services/log-service.ts index 1e72398..6e32f7a 100644 --- a/src/renderer/services/log-service.ts +++ b/src/renderer/services/log-service.ts @@ -175,6 +175,26 @@ export function logThink(thinking: string): void { } export function logStream(msg: string): void { addLog('stream', msg); } + +/** 更新流式进度日志(原地更新,不追加新条目) */ +export function logStreamProgress(msg: string): void { + if (!logBodyEl) { addLog('stream', msg); return; } + const existing = document.getElementById('log-stream-progress'); + if (existing) { + // 更新已有条目 + const timeSpan = existing.querySelector('.log-time'); + if (timeSpan) timeSpan.textContent = formatTime(Date.now()); + const msgSpan = existing.querySelector('.log-msg'); + if (msgSpan) msgSpan.textContent = msg; + } else { + // 创建新条目 — 直接 addLog 然后手动修改 id + addLog('stream', msg); + // 最后一个日志条目就是我们刚加的 + const lastEntry = logBodyEl.querySelector('.log-stream:last-of-type'); + if (lastEntry) lastEntry.id = 'log-stream-progress'; + } +} + export function logAgentLoop(iteration: number, maxLoops: number): void { addLog('info', `Loop #${iteration}/${maxLoops}`); } export function logModelResponse(contentLen: number, toolCalls: number): void { addLog('stream', `响应: ${contentLen}字, ${toolCalls}工具`); } export function logConnection(status: string, detail?: string): void { addLog(status === 'connected' ? 'success' : 'warn', `连接: ${status}`, detail); }