fix: 流式日志原地更新 + 滚动用scrollIntoView
- 新增 logStreamProgress:同一ID日志更新不追加 - 定时器15s间隔,无新内容用累积闲置时长 - appendToolCardDOM 用 scrollIntoView + rAF - renderToolCalls 用双重 rAF 等布局稳定
This commit is contained in:
@@ -833,7 +833,7 @@ function appendToolCardDOM(tc: ToolCallRecord): void {
|
|||||||
container.appendChild(card);
|
container.appendChild(card);
|
||||||
// 等浏览器完成布局后再滚到底部
|
// 等浏览器完成布局后再滚到底部
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
container.scrollTop = container.scrollHeight;
|
container.lastElementChild?.scrollIntoView({ block: 'end' });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -938,9 +938,11 @@ function renderToolCalls(): void {
|
|||||||
html += renderToolCard(tc);
|
html += renderToolCard(tc);
|
||||||
}
|
}
|
||||||
container.innerHTML = html;
|
container.innerHTML = html;
|
||||||
// 等浏览器完成布局后再滚到底部
|
// 全量重建后等两帧让布局稳定,再滚到底部
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
container.scrollTop = container.scrollHeight;
|
requestAnimationFrame(() => {
|
||||||
|
container.lastElementChild?.scrollIntoView({ block: 'end' });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
import { searchMemories, buildMemoryContext, markMemoryUsed, isMemoryEnabled } from './memory-manager.js';
|
import { searchMemories, buildMemoryContext, markMemoryUsed, isMemoryEnabled } from './memory-manager.js';
|
||||||
import { extractSkillsFromToolRecords, matchSkills, buildSkillContext } from './skill-manager.js';
|
import { extractSkillsFromToolRecords, matchSkills, buildSkillContext } from './skill-manager.js';
|
||||||
import { showToast } from '../components/toast.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 { getWorkspaceDirPath } from '../components/workspace-panel.js';
|
||||||
import { generateId } from '../utils/utils.js';
|
import { generateId } from '../utils/utils.js';
|
||||||
import { buildContext, estimateTokens, shouldAutoCompress, compressWithLLM, AUTO_COMPRESS_THRESHOLD, recordActualTokens } from './context-manager.js';
|
import { buildContext, estimateTokens, shouldAutoCompress, compressWithLLM, AUTO_COMPRESS_THRESHOLD, recordActualTokens } from './context-manager.js';
|
||||||
@@ -729,12 +729,12 @@ Shell: ${osInfo.shell}
|
|||||||
// 有新增内容 → 正常进度
|
// 有新增内容 → 正常进度
|
||||||
lastLoggedLen = content.length;
|
lastLoggedLen = content.length;
|
||||||
lastContentTime = Date.now();
|
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) {
|
} else if (content.length > 0) {
|
||||||
// 内容不变 → 计算真正的无新内容时长
|
// 内容不变 → 计算真正的无新内容时长
|
||||||
const idleSec = Math.round((Date.now() - lastContentTime) / 1000);
|
const idleSec = Math.round((Date.now() - lastContentTime) / 1000);
|
||||||
if (idleSec >= 10) {
|
if (idleSec >= 10) {
|
||||||
logStream(`⚠️ ${idleSec}s 无新内容,当前 ${content.length} 字`);
|
logStreamProgress(`⚠️ ${idleSec}s 无新内容,当前 ${content.length} 字`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, PROGRESS_INTERVAL);
|
}, PROGRESS_INTERVAL);
|
||||||
|
|||||||
@@ -175,6 +175,26 @@ export function logThink(thinking: string): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function logStream(msg: string): void { addLog('stream', msg); }
|
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 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 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); }
|
export function logConnection(status: string, detail?: string): void { addLog(status === 'connected' ? 'success' : 'warn', `连接: ${status}`, detail); }
|
||||||
|
|||||||
Reference in New Issue
Block a user