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
|
<div
|
||||||
v-if="chatDisplayMode === 'chat'"
|
v-if="chatDisplayMode === 'chat'"
|
||||||
class="scroll-lock-toggle"
|
class="scroll-lock-toggle"
|
||||||
:class="{ locked: autoScrollEnabled && !userScrolling }"
|
:class="{ locked: autoScrollEnabled && !userScrolling && isOutputActive() }"
|
||||||
>
|
>
|
||||||
<button @click="toggleScrollLock" class="scroll-lock-btn">
|
<button @click="toggleScrollLock" class="scroll-lock-btn">
|
||||||
<svg
|
<svg
|
||||||
v-if="autoScrollEnabled && !userScrolling"
|
v-if="autoScrollEnabled && !userScrolling && isOutputActive()"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
|
|||||||
@ -709,6 +709,9 @@ const appOptions = {
|
|||||||
}
|
}
|
||||||
return elRef || null;
|
return elRef || null;
|
||||||
},
|
},
|
||||||
|
isOutputActive() {
|
||||||
|
return !!(this.streamingMessage || this.taskInProgress || this.hasPendingToolActions());
|
||||||
|
},
|
||||||
logMessageState(action, extra = {}) {
|
logMessageState(action, extra = {}) {
|
||||||
const count = Array.isArray(this.messages) ? this.messages.length : 'N/A';
|
const count = Array.isArray(this.messages) ? this.messages.length : 'N/A';
|
||||||
debugLog('[Messages]', {
|
debugLog('[Messages]', {
|
||||||
@ -999,11 +1002,26 @@ const appOptions = {
|
|||||||
const clientHeight = messagesArea.clientHeight;
|
const clientHeight = messagesArea.clientHeight;
|
||||||
const isAtBottom = scrollHeight - scrollTop - clientHeight < bottomThreshold;
|
const isAtBottom = scrollHeight - scrollTop - clientHeight < bottomThreshold;
|
||||||
|
|
||||||
if (isAtBottom) {
|
const activeLock = this.autoScrollEnabled && this.isOutputActive();
|
||||||
this.chatSetScrollState({ userScrolling: false, autoScrollEnabled: true });
|
|
||||||
} else {
|
// 锁定且当前有输出时,强制贴底
|
||||||
this.chatSetScrollState({ userScrolling: true, autoScrollEnabled: false });
|
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;
|
_setScrollingFlag?: (value: boolean) => void;
|
||||||
autoScrollEnabled?: boolean;
|
autoScrollEnabled?: boolean;
|
||||||
userScrolling?: boolean;
|
userScrolling?: boolean;
|
||||||
|
isOutputActive?: () => boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function scrollToBottom(ctx: ScrollContext) {
|
export function scrollToBottom(ctx: ScrollContext) {
|
||||||
@ -46,14 +47,16 @@ export function scrollToBottom(ctx: ScrollContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function conditionalScrollToBottom(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);
|
scrollToBottom(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toggleScrollLock(ctx: ScrollContext) {
|
export function toggleScrollLock(ctx: ScrollContext) {
|
||||||
const nextState = ctx.chatToggleScrollLockState?.() ?? false;
|
const nextState = ctx.chatToggleScrollLockState?.() ?? false;
|
||||||
if (nextState) {
|
const active = typeof ctx.isOutputActive === 'function' ? ctx.isOutputActive() : true;
|
||||||
|
if (nextState && active) {
|
||||||
scrollToBottom(ctx);
|
scrollToBottom(ctx);
|
||||||
}
|
}
|
||||||
return nextState;
|
return nextState;
|
||||||
|
|||||||
@ -144,10 +144,11 @@ export const useChatStore = defineStore('chat', {
|
|||||||
this.setScrollState({ autoScrollEnabled: true, userScrolling: false });
|
this.setScrollState({ autoScrollEnabled: true, userScrolling: false });
|
||||||
},
|
},
|
||||||
disableAutoScroll() {
|
disableAutoScroll() {
|
||||||
this.setScrollState({ autoScrollEnabled: false, userScrolling: true });
|
this.setScrollState({ autoScrollEnabled: false, userScrolling: false });
|
||||||
},
|
},
|
||||||
toggleScrollLockState() {
|
toggleScrollLockState() {
|
||||||
if (this.isScrollLocked) {
|
const locked = this.isScrollLocked;
|
||||||
|
if (locked) {
|
||||||
this.disableAutoScroll();
|
this.disableAutoScroll();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -268,6 +268,13 @@
|
|||||||
transition:
|
transition:
|
||||||
max-height 0.26s cubic-bezier(0.4, 0, 0.2, 1),
|
max-height 0.26s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
opacity 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 {
|
.collapsible-block.expanded .collapsible-content {
|
||||||
|
|||||||