From b9a2170fce1f3f33cb4934b34efecb806bbbb348 Mon Sep 17 00:00:00 2001 From: Aleksander Grygier Date: Mon, 18 May 2026 16:17:21 +0200 Subject: [PATCH] feat: add scroll-to-bottom button to chat + prevent forced scroll down (#23270) --- .../app/chat/ChatScreen/ChatScreen.svelte | 9 +++- .../ChatScreenActionScrollDown.svelte | 48 +++++++++++++++++++ tools/ui/src/lib/components/app/chat/index.ts | 7 +++ .../src/lib/hooks/use-auto-scroll.svelte.ts | 8 ++++ 4 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenActionScrollDown.svelte diff --git a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte index dc3eab134..bd93a569c 100644 --- a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte +++ b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreen.svelte @@ -8,6 +8,7 @@ ChatMessages, ChatScreenDragOverlay, ChatScreenProcessingInfo, + ChatScreenActionScrollDown, DialogEmptyFileAlert, DialogFileUploadError, DialogChatError, @@ -338,7 +339,9 @@ }); function handleMessagesReady() { - if (!disableAutoScroll && !autoScroll.userScrolledUp) { + if (disableAutoScroll) return; + + if (!autoScroll.userScrolledUp) { requestAnimationFrame(() => { autoScroll.scrollToBottom('instant'); }); @@ -405,7 +408,7 @@
{#if isEmpty}
@@ -419,6 +422,8 @@
{/if} + + {#if page.params.id} {/if} diff --git a/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenActionScrollDown.svelte b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenActionScrollDown.svelte new file mode 100644 index 000000000..3f3ee8677 --- /dev/null +++ b/tools/ui/src/lib/components/app/chat/ChatScreen/ChatScreenActionScrollDown.svelte @@ -0,0 +1,48 @@ + + +
+ +
diff --git a/tools/ui/src/lib/components/app/chat/index.ts b/tools/ui/src/lib/components/app/chat/index.ts index 5f6597980..9c7ce864e 100644 --- a/tools/ui/src/lib/components/app/chat/index.ts +++ b/tools/ui/src/lib/components/app/chat/index.ts @@ -667,3 +667,10 @@ export { default as ChatScreenForm } from './ChatScreen/ChatScreenForm.svelte'; * Only visible when `isCurrentConversationLoading` is true. */ export { default as ChatScreenProcessingInfo } from './ChatScreen/ChatScreenProcessingInfo.svelte'; + +/** + * Scroll-to-bottom action button. Displays a floating button when the user + * has scrolled up more than half a viewport height from the bottom. + * Takes the chat container element as a prop to manage scroll state internally. + */ +export { default as ChatScreenActionScrollDown } from './ChatScreen/ChatScreenActionScrollDown.svelte'; diff --git a/tools/ui/src/lib/hooks/use-auto-scroll.svelte.ts b/tools/ui/src/lib/hooks/use-auto-scroll.svelte.ts index f59e3ed4b..7bac452e4 100644 --- a/tools/ui/src/lib/hooks/use-auto-scroll.svelte.ts +++ b/tools/ui/src/lib/hooks/use-auto-scroll.svelte.ts @@ -100,6 +100,14 @@ export class AutoScrollController { this._autoScrollEnabled = true; } + /** + * Resets scroll state when switching conversations. + */ + resetScrollState(): void { + this._userScrolledUp = false; + this._autoScrollEnabled = true; + } + /** * Starts the auto-scroll interval for continuous scrolling during streaming. */