fix: unify collapse animation and scroll lock reset
This commit is contained in:
parent
95285747c0
commit
d34fbe963a
@ -57,6 +57,7 @@ import {
|
||||
scrollToBottom as scrollToBottomHelper,
|
||||
conditionalScrollToBottom as conditionalScrollToBottomHelper,
|
||||
toggleScrollLock as toggleScrollLockHelper,
|
||||
normalizeScrollLock,
|
||||
scrollThinkingToBottom as scrollThinkingToBottomHelper
|
||||
} from './composables/useScrollControl';
|
||||
import {
|
||||
@ -294,7 +295,7 @@ const appOptions = {
|
||||
});
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
async mounted() {
|
||||
debugLog('Vue应用已挂载');
|
||||
if (window.ensureCsrfToken) {
|
||||
window.ensureCsrfToken().catch((err) => {
|
||||
@ -308,6 +309,8 @@ const appOptions = {
|
||||
await socketPromise;
|
||||
this.$nextTick(() => {
|
||||
this.ensureScrollListener();
|
||||
// 刷新后若无输出,自动解锁滚动锁定
|
||||
normalizeScrollLock(this);
|
||||
});
|
||||
setupShowImageObserver();
|
||||
|
||||
|
||||
@ -79,7 +79,10 @@
|
||||
</div>
|
||||
<span class="status-text">{{ group.action.streaming ? '正在思考...' : '思考过程' }}</span>
|
||||
</div>
|
||||
<div class="collapsible-content">
|
||||
<div
|
||||
class="collapsible-content"
|
||||
:ref="el => registerCollapseContent(group.action.blockId || `${index}-thinking-${group.actionIndex}`, el)"
|
||||
>
|
||||
<div
|
||||
class="content-inner thinking-content"
|
||||
:ref="el => registerThinkingRef(group.action.blockId || `${index}-thinking-${group.actionIndex}`, el)"
|
||||
@ -208,7 +211,10 @@
|
||||
</div>
|
||||
<span class="status-text">{{ action.streaming ? '正在思考...' : '思考过程' }}</span>
|
||||
</div>
|
||||
<div class="collapsible-content">
|
||||
<div
|
||||
class="collapsible-content"
|
||||
:ref="el => registerCollapseContent(action.blockId || `${index}-thinking-${actionIndex}`, el)"
|
||||
>
|
||||
<div
|
||||
class="content-inner thinking-content"
|
||||
:ref="el => registerThinkingRef(action.blockId || `${index}-thinking-${actionIndex}`, el)"
|
||||
@ -319,7 +325,10 @@
|
||||
</div>
|
||||
<span class="status-text">系统消息</span>
|
||||
</div>
|
||||
<div class="collapsible-content">
|
||||
<div
|
||||
class="collapsible-content"
|
||||
:ref="el => registerCollapseContent(`system-${index}`, el)"
|
||||
>
|
||||
<div class="content-inner">
|
||||
{{ msg.content }}
|
||||
</div>
|
||||
@ -359,6 +368,17 @@ const stackedBlocksEnabled = computed(() => personalization.experiments.stackedB
|
||||
const DEFAULT_GENERATING_TEXT = '生成中…';
|
||||
const rootEl = ref<HTMLElement | null>(null);
|
||||
const thinkingRefs = new Map<string, HTMLElement | null>();
|
||||
const registerCollapseContent = (key: string, el: Element | null) => {
|
||||
if (!(el instanceof HTMLElement)) {
|
||||
return;
|
||||
}
|
||||
requestAnimationFrame(() => {
|
||||
const h = el.scrollHeight || el.offsetHeight || 0;
|
||||
if (h > 0) {
|
||||
el.style.setProperty('--collapse-max', `${h}px`);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function registerThinkingRef(key: string, el: Element | null) {
|
||||
if (el instanceof HTMLElement) {
|
||||
|
||||
@ -20,7 +20,10 @@
|
||||
<span class="status-text">{{ getToolStatusText(action.tool) }}</span>
|
||||
<span class="tool-desc">{{ getToolDescription(action.tool) }}</span>
|
||||
</div>
|
||||
<div class="collapsible-content">
|
||||
<div
|
||||
class="collapsible-content"
|
||||
:ref="el => registerCollapseContent && registerCollapseContent(collapseKey || action.tool.id || action.id || 'tool', el)"
|
||||
>
|
||||
<div class="content-inner">
|
||||
<div v-if="action.tool.name === 'web_search' && action.tool.result">
|
||||
<div class="search-meta">
|
||||
@ -75,6 +78,8 @@ defineProps<{
|
||||
formatSearchTopic: (filters: Record<string, any>) => string;
|
||||
formatSearchTime: (filters: Record<string, any>) => string;
|
||||
streamingMessage: boolean;
|
||||
registerCollapseContent?: (key: string, el: Element | null) => void;
|
||||
collapseKey?: string;
|
||||
}>();
|
||||
|
||||
defineEmits<{ (event: 'toggle'): void }>();
|
||||
|
||||
@ -10,6 +10,8 @@ type ScrollContext = {
|
||||
autoScrollEnabled?: boolean;
|
||||
userScrolling?: boolean;
|
||||
isOutputActive?: () => boolean;
|
||||
streamingMessage?: boolean;
|
||||
hasPendingToolActions?: () => boolean;
|
||||
};
|
||||
|
||||
type ScrollOptions = {
|
||||
@ -106,6 +108,21 @@ export function toggleScrollLock(ctx: ScrollContext) {
|
||||
return nextState;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在页面初始化/刷新后同步滚动锁定的默认状态:
|
||||
* 仅当存在输出(流式中或有未完成工具)时才保持锁定,否则自动解锁。
|
||||
*/
|
||||
export function normalizeScrollLock(ctx: ScrollContext) {
|
||||
const active =
|
||||
(typeof ctx.isOutputActive === 'function' ? ctx.isOutputActive() : false) ||
|
||||
!!ctx.streamingMessage ||
|
||||
(typeof ctx.hasPendingToolActions === 'function' ? ctx.hasPendingToolActions() : false);
|
||||
|
||||
if (!active && ctx.autoScrollEnabled) {
|
||||
ctx.chatSetScrollState?.({ autoScrollEnabled: false, userScrolling: false });
|
||||
}
|
||||
}
|
||||
|
||||
export function scrollThinkingToBottom(ctx: ScrollContext, blockId: string) {
|
||||
if (!blockId) {
|
||||
return;
|
||||
|
||||
@ -266,11 +266,12 @@
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
transition:
|
||||
max-height 0.26s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
opacity 0.26s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
max-height 260ms cubic-bezier(0.4, 0, 0.2, 1),
|
||||
opacity 220ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
/* 始终隐藏滚动条,避免堆叠模式展开时闪现 */
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
will-change: max-height, opacity;
|
||||
}
|
||||
|
||||
.collapsible-content::-webkit-scrollbar {
|
||||
@ -278,7 +279,7 @@
|
||||
}
|
||||
|
||||
.collapsible-block.expanded .collapsible-content {
|
||||
max-height: 600px;
|
||||
max-height: var(--collapse-max, 600px);
|
||||
overflow-y: auto;
|
||||
opacity: 1;
|
||||
}
|
||||
@ -439,8 +440,8 @@
|
||||
|
||||
.stacked-block .collapsible-content {
|
||||
transition:
|
||||
max-height 280ms cubic-bezier(0.25, 0.9, 0.3, 1),
|
||||
opacity 220ms ease;
|
||||
max-height 260ms cubic-bezier(0.4, 0, 0.2, 1),
|
||||
opacity 220ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.progress-indicator {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user