fix: reduce chat debug noise and stabilize auto-scroll behavior

This commit is contained in:
JOJO 2026-04-25 16:35:09 +08:00
parent d076e334d9
commit 46404095dd
4 changed files with 60 additions and 98 deletions

View File

@ -1,6 +1,6 @@
// @ts-nocheck
const ENABLE_APP_DEBUG_LOGS = true;
const TRACE_CONV = true;
const ENABLE_APP_DEBUG_LOGS = false;
const TRACE_CONV = false;
export function debugLog(...args) {
if (!ENABLE_APP_DEBUG_LOGS) return;

View File

@ -579,7 +579,7 @@
</template>
<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
import ToolAction from '@/components/chat/actions/ToolAction.vue';
import StackedBlocks from './StackedBlocks.vue';
import MinimalBlocks from './MinimalBlocks.vue';
@ -695,17 +695,13 @@ const nowMs = ref(Date.now());
let timerHandle: number | null = null;
const debugLoggedSystemActionKeys = new Set<string>();
const debugLoggedUnknownActionKeys = new Set<string>();
const CHAT_DEBUG_LOGS = false;
const userMDebug = (...args: any[]) => {
if (!CHAT_DEBUG_LOGS) {
return;
}
console.log('[USERMDEBUG]', ...args);
};
const safeJson = (value: any) => {
try {
return JSON.stringify(value);
} catch {
return '[unserializable]';
}
};
const DEFAULT_GENERATING_TEXT = '生成中…';
const rootEl = ref<HTMLElement | null>(null);
const thinkingRefs = new Map<string, HTMLElement | null>();
@ -898,72 +894,11 @@ function assistantWorkLabel(index: number): string {
}
onMounted(() => {
userMDebug('ChatArea.mounted:tail-shape', {
filteredLength: filteredMessages.value.length,
tail: filteredMessages.value.slice(-8).map((m, i) => ({
idx: filteredMessages.value.length - 8 + i,
role: m?.role,
actionTypes: Array.isArray(m?.actions) ? m.actions.map((a: any) => a?.type) : []
}))
});
timerHandle = window.setInterval(() => {
nowMs.value = Date.now();
}, 1000);
});
watch(
filteredMessages,
(next) => {
const tail = (next || []).slice(-6).map((m: any, i: number) => {
const actionTypes = Array.isArray(m?.actions)
? m.actions.map((a: any) => `${a?.type}${a?.streaming ? ':streaming' : ''}`)
: [];
const idx = (next?.length || 0) - Math.min(6, next?.length || 0) + i;
return {
idx,
role: m?.role,
actionTypes,
isSubAgentNoticeOnlyMessage: isSubAgentNoticeOnlyMessage(m),
followedBySubAgentNotice: isFollowedBySubAgentNotice(idx)
};
});
userMDebug('ChatArea.watch:filteredMessages-changed', {
length: next?.length || 0,
tail
});
userMDebug('ChatArea.watch:filteredMessages-changed:json', safeJson({
length: next?.length || 0,
tail
}));
nextTick(() => {
requestAnimationFrame(() => {
const root = rootEl.value;
if (!root) return;
const blocks = Array.from(root.querySelectorAll('.message-block')) as HTMLElement[];
const last = blocks.slice(-4);
const geometry = last.map((el, i) => {
const r = el.getBoundingClientRect();
const prev = i > 0 ? last[i - 1].getBoundingClientRect() : null;
const gapFromPrev = prev ? Math.round((r.top - prev.bottom) * 100) / 100 : null;
return {
className: el.className,
top: Math.round(r.top * 100) / 100,
bottom: Math.round(r.bottom * 100) / 100,
height: Math.round(r.height * 100) / 100,
gapFromPrev
};
});
userMDebug('ChatArea.layout:last-blocks', {
totalBlocks: blocks.length,
geometry
});
});
});
},
{ deep: true }
);
onBeforeUnmount(() => {
if (timerHandle !== null) {
clearInterval(timerHandle);
@ -988,7 +923,7 @@ const splitActionGroups = (actions: any[] = [], messageIndex = 0) => {
let buffer: any[] = [];
//
if (actions.length > 0) {
if (CHAT_DEBUG_LOGS && actions.length > 0) {
console.log(`[splitActionGroups] 消息 ${messageIndex}:`, {
totalActions: actions.length,
actionTypes: actions.map((a) => a.type),
@ -1034,7 +969,7 @@ const splitActionGroups = (actions: any[] = [], messageIndex = 0) => {
flushBuffer();
//
if (actions.length > 0) {
if (CHAT_DEBUG_LOGS && actions.length > 0) {
console.log(`[splitActionGroups] 消息 ${messageIndex} 结果:`, {
totalGroups: result.length,
groups: result.map((g) => ({

View File

@ -27,31 +27,45 @@ type ScrollOptions = {
behavior?: ScrollBehavior;
};
type PendingScrollTask = {
rafId: number | null;
settleTimer: ReturnType<typeof setTimeout> | null;
};
const pendingScrollTasks = new WeakMap<HTMLElement, PendingScrollTask>();
let autoScrollRafId: number | null = null;
export function scrollToBottom(ctx: ScrollContext, options?: ScrollOptions) {
const messagesArea = ctx.getMessagesAreaElement?.();
if (!messagesArea) {
return;
}
const attempts = [0, 16, 60, 150, 320, 520]; // 多次尝试覆盖布局抖动/异步伸缩
let cancelled = false;
const useSmooth =
options?.behavior === 'smooth' && typeof (messagesArea as HTMLElement).scrollTo === 'function';
const perform = (idx: number) => {
if (cancelled) return;
let task = pendingScrollTasks.get(messagesArea);
if (!task) {
task = { rafId: null, settleTimer: null };
pendingScrollTasks.set(messagesArea, task);
}
if (task.rafId !== null) {
cancelAnimationFrame(task.rafId);
task.rafId = null;
}
if (task.settleTimer) {
clearTimeout(task.settleTimer);
task.settleTimer = null;
}
const perform = () => {
if (ctx.userScrolling && !options?.ignoreUserScrolling) {
cancelled = true;
if (typeof ctx._setScrollingFlag === 'function') {
ctx._setScrollingFlag(false);
}
return;
}
if (idx === 0 && typeof ctx._setScrollingFlag === 'function') {
ctx._setScrollingFlag(true);
}
if (useSmooth) {
(messagesArea as HTMLElement).scrollTo({
top: messagesArea.scrollHeight,
@ -61,10 +75,12 @@ export function scrollToBottom(ctx: ScrollContext, options?: ScrollOptions) {
messagesArea.scrollTop = messagesArea.scrollHeight;
}
if (idx < attempts.length - 1) {
setTimeout(() => perform(idx + 1), attempts[idx + 1] - attempts[idx]);
} else if (typeof ctx._setScrollingFlag === 'function') {
setTimeout(() => ctx._setScrollingFlag && ctx._setScrollingFlag(false), 40);
task!.settleTimer = setTimeout(() => {
// 补一次,覆盖图片/折叠动画等异步高度变化
messagesArea.scrollTop = messagesArea.scrollHeight;
if (typeof ctx._setScrollingFlag === 'function') {
ctx._setScrollingFlag(false);
}
if (options?.resetUserScrolling) {
if (typeof ctx.chatSetScrollState === 'function') {
ctx.chatSetScrollState({ userScrolling: false });
@ -72,22 +88,33 @@ export function scrollToBottom(ctx: ScrollContext, options?: ScrollOptions) {
ctx.userScrolling = false;
}
}
} else if (options?.resetUserScrolling) {
if (typeof ctx.chatSetScrollState === 'function') {
ctx.chatSetScrollState({ userScrolling: false });
} else if ('userScrolling' in ctx) {
ctx.userScrolling = false;
}
}
task!.settleTimer = null;
}, 120);
};
perform(0);
if (typeof ctx._setScrollingFlag === 'function') {
ctx._setScrollingFlag(true);
}
task.rafId = requestAnimationFrame(() => {
task!.rafId = null;
perform();
if (!task!.settleTimer && typeof ctx._setScrollingFlag === 'function') {
ctx._setScrollingFlag(false);
}
});
}
export function conditionalScrollToBottom(ctx: ScrollContext) {
const active = typeof ctx.isOutputActive === 'function' ? ctx.isOutputActive() : true;
if (ctx.autoScrollEnabled === true && ctx.userScrolling === false && active) {
scrollToBottom(ctx);
if (autoScrollRafId !== null) {
cancelAnimationFrame(autoScrollRafId);
}
autoScrollRafId = requestAnimationFrame(() => {
autoScrollRafId = null;
scrollToBottom(ctx);
});
}
}

View File

@ -20,14 +20,14 @@ const monitorTrace = (label: string) => {
}
console.trace(`[Monitor] ${label}`);
};
const MONITOR_PROGRESS_DEBUG = true;
const MONITOR_PROGRESS_DEBUG = false;
const monitorProgressDebug = (...args: any[]) => {
if (!MONITOR_PROGRESS_DEBUG) {
return;
}
console.debug('[MonitorProgressStore]', ...args);
};
const MONITOR_LIFECYCLE_DEBUG = true;
const MONITOR_LIFECYCLE_DEBUG = false;
const monitorLifecycleLog = (...args: any[]) => {
if (!MONITOR_LIFECYCLE_DEBUG) {
return;