refactor(chat): replace useSnapScroll with StickToBottom for smoother scrolling

The useSnapScroll hook has been replaced with the StickToBottom component to improve the scrolling behavior in the chat interface. This change ensures smoother and more consistent scrolling, especially when new messages are added. The StickToBottom component provides better control over the scroll position and handles edge cases more effectively.
This commit is contained in:
KevIsDev
2025-04-22 21:33:40 +01:00
parent b41691f6f2
commit b009b02057
7 changed files with 791 additions and 183 deletions

View File

@@ -42,6 +42,7 @@ import { SupabaseConnection } from './SupabaseConnection';
import { ExpoQrModal } from '~/components/workbench/ExpoQrModal';
import { expoUrlAtom } from '~/lib/stores/qrCodeStore';
import { useStore } from '@nanostores/react';
import { StickToBottom } from '~/lib/hooks';
const TEXTAREA_MIN_HEIGHT = 76;
@@ -87,8 +88,6 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
(
{
textareaRef,
messageRef,
scrollRef,
showChat = true,
chatStarted = false,
isStreaming = false,
@@ -336,7 +335,7 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
data-chat-visible={showChat}
>
<ClientOnly>{() => <Menu />}</ClientOnly>
<div ref={scrollRef} className="flex flex-col lg:flex-row overflow-y-auto w-full h-full">
<div className="flex flex-col lg:flex-row overflow-y-auto w-full h-full">
<div className={classNames(styles.Chat, 'flex flex-col flex-grow lg:min-w-[var(--chat-min-width)] h-full')}>
{!chatStarted && (
<div id="intro" className="mt-[16vh] max-w-chat mx-auto text-center px-4 lg:px-0">
@@ -348,24 +347,26 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
</p>
</div>
)}
<div
className={classNames('pt-6 px-2 sm:px-6', {
<StickToBottom
className={classNames('pt-6 px-2 sm:px-6 relative', {
'h-full flex flex-col': chatStarted,
})}
ref={scrollRef}
resize="smooth"
initial="smooth"
>
<ClientOnly>
{() => {
return chatStarted ? (
<Messages
ref={messageRef}
className="flex flex-col w-full flex-1 max-w-chat pb-6 mx-auto z-1"
messages={messages}
isStreaming={isStreaming}
/>
) : null;
}}
</ClientOnly>
<StickToBottom.Content className="flex flex-col gap-4">
<ClientOnly>
{() => {
return chatStarted ? (
<Messages
className="flex flex-col w-full flex-1 max-w-chat pb-6 mx-auto z-1"
messages={messages}
isStreaming={isStreaming}
/>
) : null;
}}
</ClientOnly>
</StickToBottom.Content>
{deployAlert && (
<DeployChatAlert
alert={deployAlert}
@@ -387,7 +388,7 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
/>
)}
<div
className={classNames('flex flex-col gap-2 w-full max-w-chat mx-auto z-prompt mb-6', {
className={classNames('my-auto flex flex-col gap-2 w-full max-w-chat mx-auto z-prompt mb-6', {
'sticky bottom-2': chatStarted,
})}
>
@@ -639,7 +640,7 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
</div>
</div>
</div>
</div>
</StickToBottom>
<div className="flex flex-col justify-center gap-5">
{!chatStarted && (
<div className="flex justify-center gap-2">

View File

@@ -8,7 +8,7 @@ import { useChat } from 'ai/react';
import { useAnimate } from 'framer-motion';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { cssTransition, toast, ToastContainer } from 'react-toastify';
import { useMessageParser, usePromptEnhancer, useShortcuts, useSnapScroll } from '~/lib/hooks';
import { useMessageParser, usePromptEnhancer, useShortcuts } from '~/lib/hooks';
import { description, useChatHistory } from '~/lib/persistence';
import { chatStore } from '~/lib/stores/chat';
import { workbenchStore } from '~/lib/stores/workbench';
@@ -483,8 +483,6 @@ export const ChatImpl = memo(
[],
);
const [messageRef, scrollRef] = useSnapScroll();
useEffect(() => {
const storedApiKeys = Cookies.get('apiKeys');
@@ -522,8 +520,6 @@ export const ChatImpl = memo(
provider={provider}
setProvider={handleProviderChange}
providerList={activeProviders}
messageRef={messageRef}
scrollRef={scrollRef}
handleInputChange={(e) => {
onTextareaChange(e);
debouncedCachePrompt(e);

View File

@@ -20,8 +20,8 @@ export const ExpoQrModal: React.FC<ExpoQrModalProps> = ({ open, onClose }) => {
onClose={onClose}
>
<div className="border !border-bolt-elements-borderColor flex flex-col gap-5 justify-center items-center p-6 bg-bolt-elements-background-depth-2 rounded-md">
<div className="i-bolt:expo-brand h-10 w-full"></div>
<DialogTitle className="text-white text-lg font-semibold leading-6">
<div className="i-bolt:expo-brand h-10 w-full invert dark:invert-none"></div>
<DialogTitle className="text-bolt-elements-textTertiary text-lg font-semibold leading-6">
Preview on your own mobile device
</DialogTitle>
<DialogDescription className="bg-bolt-elements-background-depth-3 max-w-sm rounded-md p-1 border border-bolt-elements-borderColor">