fix: reduce chat debug noise and stabilize auto-scroll behavior
This commit is contained in:
parent
d076e334d9
commit
46404095dd
@ -1,6 +1,6 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
const ENABLE_APP_DEBUG_LOGS = true;
|
const ENABLE_APP_DEBUG_LOGS = false;
|
||||||
const TRACE_CONV = true;
|
const TRACE_CONV = false;
|
||||||
|
|
||||||
export function debugLog(...args) {
|
export function debugLog(...args) {
|
||||||
if (!ENABLE_APP_DEBUG_LOGS) return;
|
if (!ENABLE_APP_DEBUG_LOGS) return;
|
||||||
|
|||||||
@ -579,7 +579,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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 ToolAction from '@/components/chat/actions/ToolAction.vue';
|
||||||
import StackedBlocks from './StackedBlocks.vue';
|
import StackedBlocks from './StackedBlocks.vue';
|
||||||
import MinimalBlocks from './MinimalBlocks.vue';
|
import MinimalBlocks from './MinimalBlocks.vue';
|
||||||
@ -695,17 +695,13 @@ const nowMs = ref(Date.now());
|
|||||||
let timerHandle: number | null = null;
|
let timerHandle: number | null = null;
|
||||||
const debugLoggedSystemActionKeys = new Set<string>();
|
const debugLoggedSystemActionKeys = new Set<string>();
|
||||||
const debugLoggedUnknownActionKeys = new Set<string>();
|
const debugLoggedUnknownActionKeys = new Set<string>();
|
||||||
|
const CHAT_DEBUG_LOGS = false;
|
||||||
const userMDebug = (...args: any[]) => {
|
const userMDebug = (...args: any[]) => {
|
||||||
|
if (!CHAT_DEBUG_LOGS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
console.log('[USERMDEBUG]', ...args);
|
console.log('[USERMDEBUG]', ...args);
|
||||||
};
|
};
|
||||||
const safeJson = (value: any) => {
|
|
||||||
try {
|
|
||||||
return JSON.stringify(value);
|
|
||||||
} catch {
|
|
||||||
return '[unserializable]';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const DEFAULT_GENERATING_TEXT = '生成中…';
|
const DEFAULT_GENERATING_TEXT = '生成中…';
|
||||||
const rootEl = ref<HTMLElement | null>(null);
|
const rootEl = ref<HTMLElement | null>(null);
|
||||||
const thinkingRefs = new Map<string, HTMLElement | null>();
|
const thinkingRefs = new Map<string, HTMLElement | null>();
|
||||||
@ -898,72 +894,11 @@ function assistantWorkLabel(index: number): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
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(() => {
|
timerHandle = window.setInterval(() => {
|
||||||
nowMs.value = Date.now();
|
nowMs.value = Date.now();
|
||||||
}, 1000);
|
}, 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(() => {
|
onBeforeUnmount(() => {
|
||||||
if (timerHandle !== null) {
|
if (timerHandle !== null) {
|
||||||
clearInterval(timerHandle);
|
clearInterval(timerHandle);
|
||||||
@ -988,7 +923,7 @@ const splitActionGroups = (actions: any[] = [], messageIndex = 0) => {
|
|||||||
let buffer: any[] = [];
|
let buffer: any[] = [];
|
||||||
|
|
||||||
// 调试:记录输入
|
// 调试:记录输入
|
||||||
if (actions.length > 0) {
|
if (CHAT_DEBUG_LOGS && actions.length > 0) {
|
||||||
console.log(`[splitActionGroups] 消息 ${messageIndex}:`, {
|
console.log(`[splitActionGroups] 消息 ${messageIndex}:`, {
|
||||||
totalActions: actions.length,
|
totalActions: actions.length,
|
||||||
actionTypes: actions.map((a) => a.type),
|
actionTypes: actions.map((a) => a.type),
|
||||||
@ -1034,7 +969,7 @@ const splitActionGroups = (actions: any[] = [], messageIndex = 0) => {
|
|||||||
flushBuffer();
|
flushBuffer();
|
||||||
|
|
||||||
// 调试:记录输出
|
// 调试:记录输出
|
||||||
if (actions.length > 0) {
|
if (CHAT_DEBUG_LOGS && actions.length > 0) {
|
||||||
console.log(`[splitActionGroups] 消息 ${messageIndex} 结果:`, {
|
console.log(`[splitActionGroups] 消息 ${messageIndex} 结果:`, {
|
||||||
totalGroups: result.length,
|
totalGroups: result.length,
|
||||||
groups: result.map((g) => ({
|
groups: result.map((g) => ({
|
||||||
|
|||||||
@ -27,31 +27,45 @@ type ScrollOptions = {
|
|||||||
behavior?: ScrollBehavior;
|
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) {
|
export function scrollToBottom(ctx: ScrollContext, options?: ScrollOptions) {
|
||||||
const messagesArea = ctx.getMessagesAreaElement?.();
|
const messagesArea = ctx.getMessagesAreaElement?.();
|
||||||
if (!messagesArea) {
|
if (!messagesArea) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const attempts = [0, 16, 60, 150, 320, 520]; // 多次尝试覆盖布局抖动/异步伸缩
|
|
||||||
let cancelled = false;
|
|
||||||
const useSmooth =
|
const useSmooth =
|
||||||
options?.behavior === 'smooth' && typeof (messagesArea as HTMLElement).scrollTo === 'function';
|
options?.behavior === 'smooth' && typeof (messagesArea as HTMLElement).scrollTo === 'function';
|
||||||
|
|
||||||
const perform = (idx: number) => {
|
let task = pendingScrollTasks.get(messagesArea);
|
||||||
if (cancelled) return;
|
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) {
|
if (ctx.userScrolling && !options?.ignoreUserScrolling) {
|
||||||
cancelled = true;
|
|
||||||
if (typeof ctx._setScrollingFlag === 'function') {
|
if (typeof ctx._setScrollingFlag === 'function') {
|
||||||
ctx._setScrollingFlag(false);
|
ctx._setScrollingFlag(false);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idx === 0 && typeof ctx._setScrollingFlag === 'function') {
|
|
||||||
ctx._setScrollingFlag(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (useSmooth) {
|
if (useSmooth) {
|
||||||
(messagesArea as HTMLElement).scrollTo({
|
(messagesArea as HTMLElement).scrollTo({
|
||||||
top: messagesArea.scrollHeight,
|
top: messagesArea.scrollHeight,
|
||||||
@ -61,10 +75,12 @@ export function scrollToBottom(ctx: ScrollContext, options?: ScrollOptions) {
|
|||||||
messagesArea.scrollTop = messagesArea.scrollHeight;
|
messagesArea.scrollTop = messagesArea.scrollHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idx < attempts.length - 1) {
|
task!.settleTimer = setTimeout(() => {
|
||||||
setTimeout(() => perform(idx + 1), attempts[idx + 1] - attempts[idx]);
|
// 补一次,覆盖图片/折叠动画等异步高度变化
|
||||||
} else if (typeof ctx._setScrollingFlag === 'function') {
|
messagesArea.scrollTop = messagesArea.scrollHeight;
|
||||||
setTimeout(() => ctx._setScrollingFlag && ctx._setScrollingFlag(false), 40);
|
if (typeof ctx._setScrollingFlag === 'function') {
|
||||||
|
ctx._setScrollingFlag(false);
|
||||||
|
}
|
||||||
if (options?.resetUserScrolling) {
|
if (options?.resetUserScrolling) {
|
||||||
if (typeof ctx.chatSetScrollState === 'function') {
|
if (typeof ctx.chatSetScrollState === 'function') {
|
||||||
ctx.chatSetScrollState({ userScrolling: false });
|
ctx.chatSetScrollState({ userScrolling: false });
|
||||||
@ -72,22 +88,33 @@ export function scrollToBottom(ctx: ScrollContext, options?: ScrollOptions) {
|
|||||||
ctx.userScrolling = false;
|
ctx.userScrolling = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (options?.resetUserScrolling) {
|
task!.settleTimer = null;
|
||||||
if (typeof ctx.chatSetScrollState === 'function') {
|
}, 120);
|
||||||
ctx.chatSetScrollState({ userScrolling: false });
|
|
||||||
} else if ('userScrolling' in ctx) {
|
|
||||||
ctx.userScrolling = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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) {
|
export function conditionalScrollToBottom(ctx: ScrollContext) {
|
||||||
const active = typeof ctx.isOutputActive === 'function' ? ctx.isOutputActive() : true;
|
const active = typeof ctx.isOutputActive === 'function' ? ctx.isOutputActive() : true;
|
||||||
if (ctx.autoScrollEnabled === true && ctx.userScrolling === false && active) {
|
if (ctx.autoScrollEnabled === true && ctx.userScrolling === false && active) {
|
||||||
scrollToBottom(ctx);
|
if (autoScrollRafId !== null) {
|
||||||
|
cancelAnimationFrame(autoScrollRafId);
|
||||||
|
}
|
||||||
|
autoScrollRafId = requestAnimationFrame(() => {
|
||||||
|
autoScrollRafId = null;
|
||||||
|
scrollToBottom(ctx);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,14 +20,14 @@ const monitorTrace = (label: string) => {
|
|||||||
}
|
}
|
||||||
console.trace(`[Monitor] ${label}`);
|
console.trace(`[Monitor] ${label}`);
|
||||||
};
|
};
|
||||||
const MONITOR_PROGRESS_DEBUG = true;
|
const MONITOR_PROGRESS_DEBUG = false;
|
||||||
const monitorProgressDebug = (...args: any[]) => {
|
const monitorProgressDebug = (...args: any[]) => {
|
||||||
if (!MONITOR_PROGRESS_DEBUG) {
|
if (!MONITOR_PROGRESS_DEBUG) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.debug('[MonitorProgressStore]', ...args);
|
console.debug('[MonitorProgressStore]', ...args);
|
||||||
};
|
};
|
||||||
const MONITOR_LIFECYCLE_DEBUG = true;
|
const MONITOR_LIFECYCLE_DEBUG = false;
|
||||||
const monitorLifecycleLog = (...args: any[]) => {
|
const monitorLifecycleLog = (...args: any[]) => {
|
||||||
if (!MONITOR_LIFECYCLE_DEBUG) {
|
if (!MONITOR_LIFECYCLE_DEBUG) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user