chore: snapshot before collapse fix
@ -1,18 +0,0 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M12 8V4H8" />
|
||||
<rect width="16" height="12" x="4" y="8" rx="2" />
|
||||
<path d="M2 14h2" />
|
||||
<path d="M20 14h2" />
|
||||
<path d="M15 13v2" />
|
||||
<path d="M9 13v2" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 380 B |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="48" height="48" fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="opacity:1;"><path d="M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z"/><path d="M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12"/><path d="M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17"/></svg>
|
||||
|
Before Width: | Height: | Size: 491 B |
@ -1,15 +0,0 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M4 5h16" />
|
||||
<path d="M4 12h16" />
|
||||
<path d="M4 19h16" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 279 B |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="48" height="48" fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="opacity:1;"><path d="M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11zm7.318-19.539l-10.94 10.939"/></svg>
|
||||
|
Before Width: | Height: | Size: 372 B |
@ -1,5 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M6 7c0-2 1.6-3.6 3.6-3.6h13c2 0 3.6 1.6 3.6 3.6v10c0 2-1.6 3.6-3.6 3.6h-6.2L12.4 25l0.7-4.4H9.6c-2 0-3.6-1.6-3.6-3.6V7Z" />
|
||||
<path d="M10.4 11h11.2" />
|
||||
<path d="M10.4 15h7.2" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 360 B |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="48" height="48" fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="opacity:1;"><path d="m21 21l-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg>
|
||||
|
Before Width: | Height: | Size: 261 B |
@ -1,14 +0,0 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" />
|
||||
<path d="m15 5 4 4" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 377 B |
@ -1,14 +0,0 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" />
|
||||
<circle cx="12" cy="7" r="4" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 299 B |
@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="48" height="48" fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="opacity:1;"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3m.08 4h.01"/></svg>
|
||||
|
Before Width: | Height: | Size: 291 B |
@ -135,11 +135,11 @@
|
||||
<div
|
||||
v-if="chatDisplayMode === 'chat'"
|
||||
class="scroll-lock-toggle"
|
||||
:class="{ locked: autoScrollEnabled && !userScrolling }"
|
||||
:class="{ locked: autoScrollEnabled && !userScrolling && isOutputActive() }"
|
||||
>
|
||||
<button @click="toggleScrollLock" class="scroll-lock-btn">
|
||||
<svg
|
||||
v-if="autoScrollEnabled && !userScrolling"
|
||||
v-if="autoScrollEnabled && !userScrolling && isOutputActive()"
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
stroke-linecap="round"
|
||||
|
||||
@ -709,6 +709,9 @@ const appOptions = {
|
||||
}
|
||||
return elRef || null;
|
||||
},
|
||||
isOutputActive() {
|
||||
return !!(this.streamingMessage || this.taskInProgress || this.hasPendingToolActions());
|
||||
},
|
||||
logMessageState(action, extra = {}) {
|
||||
const count = Array.isArray(this.messages) ? this.messages.length : 'N/A';
|
||||
debugLog('[Messages]', {
|
||||
@ -999,11 +1002,26 @@ const appOptions = {
|
||||
const clientHeight = messagesArea.clientHeight;
|
||||
const isAtBottom = scrollHeight - scrollTop - clientHeight < bottomThreshold;
|
||||
|
||||
if (isAtBottom) {
|
||||
this.chatSetScrollState({ userScrolling: false, autoScrollEnabled: true });
|
||||
} else {
|
||||
this.chatSetScrollState({ userScrolling: true, autoScrollEnabled: false });
|
||||
const activeLock = this.autoScrollEnabled && this.isOutputActive();
|
||||
|
||||
// 锁定且当前有输出时,强制贴底
|
||||
if (activeLock) {
|
||||
if (!isAtBottom) {
|
||||
if (typeof this._setScrollingFlag === 'function') {
|
||||
this._setScrollingFlag(true);
|
||||
}
|
||||
messagesArea.scrollTop = messagesArea.scrollHeight;
|
||||
if (typeof this._setScrollingFlag === 'function') {
|
||||
setTimeout(() => this._setScrollingFlag && this._setScrollingFlag(false), 16);
|
||||
}
|
||||
}
|
||||
// 保持锁定状态下 userScrolling 为 false
|
||||
this.chatSetScrollState({ userScrolling: false });
|
||||
return;
|
||||
}
|
||||
|
||||
// 未锁定或无输出:允许自由滚动,仅记录位置
|
||||
this.chatSetScrollState({ userScrolling: !isAtBottom });
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ type ScrollContext = {
|
||||
_setScrollingFlag?: (value: boolean) => void;
|
||||
autoScrollEnabled?: boolean;
|
||||
userScrolling?: boolean;
|
||||
isOutputActive?: () => boolean;
|
||||
};
|
||||
|
||||
export function scrollToBottom(ctx: ScrollContext) {
|
||||
@ -46,14 +47,16 @@ export function scrollToBottom(ctx: ScrollContext) {
|
||||
}
|
||||
|
||||
export function conditionalScrollToBottom(ctx: ScrollContext) {
|
||||
if (ctx.autoScrollEnabled === true && ctx.userScrolling === false) {
|
||||
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 nextState = ctx.chatToggleScrollLockState?.() ?? false;
|
||||
if (nextState) {
|
||||
const active = typeof ctx.isOutputActive === 'function' ? ctx.isOutputActive() : true;
|
||||
if (nextState && active) {
|
||||
scrollToBottom(ctx);
|
||||
}
|
||||
return nextState;
|
||||
|
||||
@ -144,10 +144,11 @@ export const useChatStore = defineStore('chat', {
|
||||
this.setScrollState({ autoScrollEnabled: true, userScrolling: false });
|
||||
},
|
||||
disableAutoScroll() {
|
||||
this.setScrollState({ autoScrollEnabled: false, userScrolling: true });
|
||||
this.setScrollState({ autoScrollEnabled: false, userScrolling: false });
|
||||
},
|
||||
toggleScrollLockState() {
|
||||
if (this.isScrollLocked) {
|
||||
const locked = this.isScrollLocked;
|
||||
if (locked) {
|
||||
this.disableAutoScroll();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -268,6 +268,13 @@
|
||||
transition:
|
||||
max-height 0.26s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
opacity 0.26s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
/* 始终隐藏滚动条,避免堆叠模式展开时闪现 */
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
}
|
||||
|
||||
.collapsible-content::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.collapsible-block.expanded .collapsible-content {
|
||||
|
||||