fix session bottom scroll alignment (#1100)
This commit is contained in:
@@ -14,6 +14,7 @@ const { t } = useI18n();
|
||||
const { isDark } = useTheme();
|
||||
const { toolTraceVisible } = useToolTraceVisibility();
|
||||
const listRef = ref<InstanceType<typeof VirtualMessageList> | null>(null);
|
||||
const pendingBottomSessionId = ref<string | null>(null);
|
||||
|
||||
function formatTokens(n: number): string {
|
||||
if (n >= 1_000_000) return (n / 1_000_000).toFixed(1) + 'M'
|
||||
@@ -115,6 +116,7 @@ watch(
|
||||
() => chatStore.activeSessionId,
|
||||
(id) => {
|
||||
if (!id) return;
|
||||
pendingBottomSessionId.value = id;
|
||||
if (chatStore.focusMessageId) {
|
||||
scrollToMessage(chatStore.focusMessageId);
|
||||
return;
|
||||
@@ -124,6 +126,20 @@ watch(
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => [chatStore.activeSessionId, chatStore.messages.length] as const,
|
||||
([id, length]) => {
|
||||
if (!id || pendingBottomSessionId.value !== id || length === 0) return;
|
||||
pendingBottomSessionId.value = null;
|
||||
if (chatStore.focusMessageId) {
|
||||
scrollToMessage(chatStore.focusMessageId);
|
||||
return;
|
||||
}
|
||||
scrollToBottom();
|
||||
},
|
||||
{ flush: "post" },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => chatStore.focusMessageId,
|
||||
(messageId) => {
|
||||
|
||||
@@ -39,6 +39,8 @@ const heightVersion = ref(0);
|
||||
const measuredHeights = new Map<string, number>();
|
||||
const observedElements = new Map<string, HTMLElement>();
|
||||
const observers = new Map<string, ResizeObserver>();
|
||||
let keepBottomUntil = 0;
|
||||
let bottomFrame: number | null = null;
|
||||
|
||||
const messageKeys = computed(() => props.messages.map(messageKey));
|
||||
|
||||
@@ -58,9 +60,10 @@ function setMeasuredHeight(key: string, height: number) {
|
||||
const oldHeight = itemHeight(key);
|
||||
if (oldHeight === height) return;
|
||||
|
||||
const el = scrollerRef.value;
|
||||
const shouldKeepBottom = !!el && (Date.now() < keepBottomUntil || isNearBottom(64));
|
||||
const index = messageKeys.value.indexOf(key);
|
||||
if (index >= 0) {
|
||||
const el = scrollerRef.value;
|
||||
if (index >= 0 && !shouldKeepBottom) {
|
||||
const rowTop = layout.value.offsets[index] || 0;
|
||||
const delta = height - oldHeight;
|
||||
if (el && rowTop < scrollTop.value && delta !== 0) {
|
||||
@@ -71,6 +74,7 @@ function setMeasuredHeight(key: string, height: number) {
|
||||
|
||||
measuredHeights.set(key, height);
|
||||
heightVersion.value += 1;
|
||||
if (shouldKeepBottom) scheduleScrollToBottom(2);
|
||||
}
|
||||
|
||||
const layout = computed(() => {
|
||||
@@ -167,14 +171,34 @@ function isNearBottom(threshold = 200): boolean {
|
||||
}
|
||||
|
||||
function scrollToBottom() {
|
||||
keepBottomUntil = Date.now() + 700;
|
||||
nextTick(() => {
|
||||
const el = scrollerRef.value;
|
||||
if (!el) return;
|
||||
el.scrollTop = el.scrollHeight;
|
||||
syncViewport();
|
||||
scheduleScrollToBottom(3);
|
||||
});
|
||||
}
|
||||
|
||||
function setScrollToBottomNow() {
|
||||
const el = scrollerRef.value;
|
||||
if (!el) return;
|
||||
el.scrollTop = Math.max(0, el.scrollHeight - el.clientHeight);
|
||||
syncViewport();
|
||||
}
|
||||
|
||||
function scheduleScrollToBottom(frames = 1) {
|
||||
if (bottomFrame != null) cancelAnimationFrame(bottomFrame);
|
||||
|
||||
const step = (remaining: number) => {
|
||||
setScrollToBottomNow();
|
||||
if (remaining <= 1) {
|
||||
bottomFrame = null;
|
||||
return;
|
||||
}
|
||||
bottomFrame = requestAnimationFrame(() => step(remaining - 1));
|
||||
};
|
||||
|
||||
bottomFrame = requestAnimationFrame(() => step(frames));
|
||||
}
|
||||
|
||||
function scrollToMessage(messageId: string) {
|
||||
const index = props.messages.findIndex(message => String(message.id) === messageId);
|
||||
if (index < 0) return;
|
||||
@@ -237,6 +261,7 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (bottomFrame != null) cancelAnimationFrame(bottomFrame);
|
||||
resizeObserver?.disconnect();
|
||||
for (const observer of observers.values()) observer.disconnect();
|
||||
observers.clear();
|
||||
|
||||
Reference in New Issue
Block a user