Compare commits

..

2 Commits

Author SHA1 Message Date
8d87068ba7 fix(chat): 修复内容大幅增长时滚动锁定不及时的问题
- 调整 vue-stick-to-bottom resize spring 参数,加快动画响应
  (damping 0.68→0.5, stiffness 0.11→0.28, mass 1.05→0.75)
- 在 scrollListener 中添加高度突变检测,当 scrollHeight 大幅增长
  (超过 200px 或 viewport 35%)且用户未脱离锁定时,用 instant
  瞬间追底,绕过 spring 动画延迟和去重机制

修复场景:思考块展开、Markdown 表格渲染等造成大幅度垂直距离增加时
滚动锁定失效或锁住不及时的问题
2026-06-09 10:47:22 +08:00
2fcd241d94 fix(frontend): 移除历史消息加载时action-item的入场动画,仅流式输出时保留动画 2026-06-09 10:27:48 +08:00
2 changed files with 38 additions and 5 deletions

View File

@ -142,7 +142,8 @@
group.action?.streaming ||
group.action?.type === 'text' ||
group.action?.type === 'thinking',
'thinking-finished': group.action?.type === 'thinking' && !group.action?.streaming
'thinking-finished': group.action?.type === 'thinking' && !group.action?.streaming,
'no-entry-animation': !streamingMessage || index !== latestMessageIndex
}"
>
<div
@ -365,7 +366,8 @@
'completed-tool': action.type === 'tool' && !action.streaming,
'immediate-show':
action.streaming || action.type === 'text' || action.type === 'thinking',
'thinking-finished': action.type === 'thinking' && !action.streaming
'thinking-finished': action.type === 'thinking' && !action.streaming,
'no-entry-animation': !streamingMessage || index !== latestMessageIndex
}"
>
<div
@ -788,9 +790,9 @@ const {
stopScroll
} = useStickToBottom({
resize: {
damping: 0.68,
stiffness: 0.11,
mass: 1.05
damping: 0.5,
stiffness: 0.28,
mass: 0.75
},
initial: 'instant'
});
@ -807,6 +809,7 @@ let lastUserWheelUpTs = 0;
let lastProgrammaticHintTs = 0;
let lastProgrammaticHintSource = '';
let suppressUserIntentUntil = 0;
let isHandlingLargeGrowth = false;
let scrollListener: ((event: Event) => void) | null = null;
let wheelListener: ((event: WheelEvent) => void) | null = null;
let traceAttachLogged = false;
@ -922,6 +925,32 @@ function attachBounceListener() {
const heightDelta = height - lastObservedHeight;
lastObservedTop = top;
lastObservedHeight = height;
// scrollHeight /
// escapedFromLock=false
// instant spring
// Markdown
const largeGrowthThreshold = Math.max(200, target.clientHeight * 0.35 || 200);
if (
!isHandlingLargeGrowth &&
heightDelta > largeGrowthThreshold &&
!escapedFromLock.value
) {
isHandlingLargeGrowth = true;
markProgrammaticHint('ChatArea.largeGrowth');
suppressUserIntentUntil = Date.now() + 900;
scrollToBottom({ animation: 'instant', preserveScrollPosition: false });
bounceTraceLog(
'large-growth:instant-scroll',
{ heightDelta, threshold: largeGrowthThreshold, ...getScrollMetrics(target) },
'large-growth:instant-scroll',
120
);
setTimeout(() => {
isHandlingLargeGrowth = false;
}, 400);
}
const now = Date.now();
const trusted = (event as any).isTrusted === true;
const closeToProgrammaticHint = now - lastProgrammaticHintTs <= 140;

View File

@ -766,6 +766,10 @@
animation: slideInFade 0.6s cubic-bezier(0.4, 0, 0.2, 1) both;
}
.action-item.no-entry-animation {
animation: none !important;
}
.action-item.streaming-content,
.action-item.immediate-show {
animation: quickFadeIn 0.2s ease-out both;