// 负责聊天区域滚动控制的通用方法,解耦 App.vue 中的 DOM 操作 type ScrollContext = { getMessagesAreaElement?: () => HTMLElement | null; getThinkingContentElement?: (blockId: string) => HTMLElement | null; chatToggleScrollLockState?: () => boolean; thinkingScrollLocks?: Map; _setScrollingFlag?: (value: boolean) => void; autoScrollEnabled?: boolean; userScrolling?: boolean; isOutputActive?: () => boolean; }; export function scrollToBottom(ctx: ScrollContext) { const messagesArea = ctx.getMessagesAreaElement?.(); if (!messagesArea) { return; } const attempts = [0, 16, 60, 150, 320, 520]; // 多次尝试覆盖布局抖动/异步伸缩 let cancelled = false; const perform = (idx: number) => { if (cancelled) return; if (ctx.userScrolling) { cancelled = true; if (typeof ctx._setScrollingFlag === 'function') { ctx._setScrollingFlag(false); } return; } if (idx === 0 && typeof ctx._setScrollingFlag === 'function') { ctx._setScrollingFlag(true); } 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); } }; perform(0); } export function conditionalScrollToBottom(ctx: ScrollContext) { const active = typeof ctx.isOutputActive === 'function' ? ctx.isOutputActive() : true; if (ctx.autoScrollEnabled === true && ctx.userScrolling === false && active) { scrollToBottom(ctx); } } export function toggleScrollLock(ctx: ScrollContext) { const active = typeof ctx.isOutputActive === 'function' ? ctx.isOutputActive() : true; // 没有模型输出时:允许点击,但不切换锁定,仅单次滚动到底部 if (!active) { scrollToBottom(ctx); return ctx.autoScrollEnabled ?? false; } const nextState = ctx.chatToggleScrollLockState?.() ?? false; if (nextState) { scrollToBottom(ctx); } return nextState; } export function scrollThinkingToBottom(ctx: ScrollContext, blockId: string) { if (!blockId) { return; } if (!ctx.thinkingScrollLocks?.get(blockId)) { return; } const el = ctx.getThinkingContentElement?.(blockId); if (el) { el.scrollTop = el.scrollHeight; } }