feat: add scroll to top/bottom buttons in chat component

This commit is contained in:
Yash 2026-04-27 00:06:38 +05:30
parent 4e2240aada
commit 6ea1cfed90
2 changed files with 84 additions and 26 deletions

View file

@ -121,9 +121,26 @@
let messageInput: MessageInput | undefined;
let autoScroll = true;
let showScrollToTop = false;
let showScrollToBottom = false;
let processing = '';
let messagesContainerElement: HTMLDivElement;
const updateScrollButtonsState = () => {
if (!messagesContainerElement) return;
const distanceFromBottom =
messagesContainerElement.scrollHeight -
messagesContainerElement.scrollTop -
messagesContainerElement.clientHeight;
const distanceFromTop = messagesContainerElement.scrollTop;
const visibilityThreshold = Math.max(120, messagesContainerElement.clientHeight * 0.5);
autoScroll = distanceFromBottom <= 5;
showScrollToBottom = distanceFromBottom > visibilityThreshold;
showScrollToTop = !autoScroll && distanceFromTop > visibilityThreshold;
};
let navbarElement;
let showEventConfirmation = false;
@ -2903,13 +2920,11 @@
<div id="chat-pane" class="flex flex-col flex-auto z-10 w-full @container overflow-auto">
{#if ($settings?.landingPageMode === 'chat' && !$selectedFolder) || createMessagesList(history, history.currentId).length > 0}
<div
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full z-10 scrollbar-hidden"
class=" pb-2.5 flex flex-col justify-between w-full flex-auto overflow-auto h-0 max-w-full z-10"
id="messages-container"
bind:this={messagesContainerElement}
on:scroll={(e) => {
autoScroll =
messagesContainerElement.scrollHeight - messagesContainerElement.scrollTop <=
messagesContainerElement.clientHeight + 5;
updateScrollButtonsState();
}}
>
<div class=" h-full w-full flex flex-col">
@ -2947,6 +2962,8 @@
bind:files
bind:prompt
bind:autoScroll
bind:showScrollToTop
bind:showScrollToBottom
bind:selectedToolIds
bind:selectedFilterIds
bind:imageGenerationEnabled

View file

@ -110,6 +110,8 @@
export let stopResponse: Function;
export let autoScroll = false;
export let showScrollToTop = false;
export let showScrollToBottom = false;
export let generating = false;
export let uploadPending = false;
@ -548,6 +550,14 @@
});
};
const scrollToTop = () => {
const element = document.getElementById('messages-container');
element.scrollTo({
top: 0,
behavior: 'smooth'
});
};
const screenCaptureHandler = async () => {
try {
// Request screen media
@ -1163,30 +1173,61 @@
: 'max-w-6xl'} w-full"
>
<div class="relative">
{#if autoScroll === false && history?.currentId}
{#if (showScrollToTop || showScrollToBottom) && history?.currentId}
<div
class=" absolute -top-12 left-0 right-0 flex justify-center z-30 pointer-events-none"
class=" absolute -top-24 left-0 right-0 flex justify-center z-30 pointer-events-none"
>
<button
class=" bg-white border border-gray-100 dark:border-none dark:bg-white/20 p-1.5 rounded-full pointer-events-auto"
on:click={() => {
autoScroll = true;
scrollToBottom();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-5 h-5"
>
<path
fill-rule="evenodd"
d="M10 3a.75.75 0 01.75.75v10.638l3.96-4.158a.75.75 0 111.08 1.04l-5.25 5.5a.75.75 0 01-1.08 0l-5.25-5.5a.75.75 0 111.08-1.04l3.96 4.158V3.75A.75.75 0 0110 3z"
clip-rule="evenodd"
/>
</svg>
</button>
<div class="flex flex-col items-center gap-2 pointer-events-auto">
{#if showScrollToTop}
<button
aria-label={$i18n.t('Go to the Top')}
title={$i18n.t('Go to the Top')}
class="bg-white border border-gray-100 dark:border-none dark:bg-white/20 p-1.5 rounded-full"
on:click={() => {
autoScroll = false;
scrollToTop();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-5 h-5"
>
<path
fill-rule="evenodd"
d="M10 17a.75.75 0 01-.75-.75V5.612L5.29 9.77a.75.75 0 11-1.08-1.04l5.25-5.5a.75.75 0 011.08 0l5.25 5.5a.75.75 0 11-1.08 1.04l-3.96-4.158V16.25A.75.75 0 0110 17z"
clip-rule="evenodd"
/>
</svg>
</button>
{/if}
{#if showScrollToBottom}
<button
aria-label={$i18n.t('Go to the Bottom')}
title={$i18n.t('Go to the Bottom')}
class="bg-white border border-gray-100 dark:border-none dark:bg-white/20 p-1.5 rounded-full"
on:click={() => {
autoScroll = true;
scrollToBottom();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-5 h-5"
>
<path
fill-rule="evenodd"
d="M10 3a.75.75 0 01.75.75v10.638l3.96-4.158a.75.75 0 111.08 1.04l-5.25 5.5a.75.75 0 01-1.08 0l-5.25-5.5a.75.75 0 111.08-1.04l3.96 4.158V3.75A.75.75 0 0110 3z"
clip-rule="evenodd"
/>
</svg>
</button>
{/if}
</div>
</div>
{/if}
</div>