fix(chat): keep composer pinned and improve scroll behavior

This commit is contained in:
ilya-bov
2026-03-22 20:56:52 +03:00
parent acf7dd17b3
commit 5db7c8e66b
4 changed files with 44 additions and 11 deletions

View File

@@ -20,13 +20,13 @@ export default async function DashboardPage() {
}
return (
<div className="[--header-height:calc(--spacing(14))]">
<SidebarProvider className="flex flex-col">
<div className="[--header-height:calc(--spacing(14))] h-svh overflow-hidden">
<SidebarProvider className="flex h-full flex-col">
<SiteHeader title="Chat" />
<div className="flex flex-1">
<div className="flex min-h-0 flex-1">
<AppSidebar />
<SidebarInset>
<div className="flex flex-1 flex-col h-[calc(100svh-var(--header-height))]">
<SidebarInset className="min-h-0">
<div className="flex min-h-0 flex-1 flex-col">
<ChatPanel />
</div>
</SidebarInset>

View File

@@ -180,9 +180,19 @@ export function ChatInput({
[chatId]
);
useEffect(() => {
if (input.length > 0) return;
const textarea = textareaRef.current;
if (!textarea) return;
// Reset textarea height back to a single-row composer after submit/clear.
textarea.style.height = "auto";
}, [input]);
return (
<div
className={`border-t bg-background p-4 transition-colors ${isDragging ? "bg-primary/5 border-primary" : ""}`}
className={`sticky bottom-0 z-20 shrink-0 border-t bg-background/95 p-4 backdrop-blur supports-[backdrop-filter]:bg-background/80 transition-colors ${isDragging ? "bg-primary/5 border-primary" : ""}`}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}

View File

@@ -1,6 +1,6 @@
"use client";
import { useEffect, useRef } from "react";
import { useCallback, useEffect, useRef } from "react";
import { MessageBubble } from "./message-bubble";
import { Loader2 } from "lucide-react";
import type { UIMessage } from "ai";
@@ -12,13 +12,32 @@ interface ChatMessagesProps {
}
export function ChatMessages({ messages, isLoading, errorMessage }: ChatMessagesProps) {
const scrollRef = useRef<HTMLDivElement>(null);
const endRef = useRef<HTMLDivElement>(null);
const shouldAutoScrollRef = useRef(true);
const AUTO_SCROLL_THRESHOLD_PX = 96;
const updateShouldAutoScroll = useCallback(() => {
const container = scrollRef.current;
if (!container) return;
const distanceFromBottom =
container.scrollHeight - container.scrollTop - container.clientHeight;
shouldAutoScrollRef.current = distanceFromBottom <= AUTO_SCROLL_THRESHOLD_PX;
}, []);
// Auto-scroll on new messages
useEffect(() => {
endRef.current?.scrollIntoView({ behavior: "smooth" });
if (!shouldAutoScrollRef.current) return;
endRef.current?.scrollIntoView({
behavior: isLoading ? "auto" : "smooth",
block: "end",
});
}, [messages, isLoading]);
useEffect(() => {
updateShouldAutoScroll();
}, [updateShouldAutoScroll]);
if (messages.length === 0 && !isLoading) {
return (
<div className="flex-1 flex items-center justify-center p-8">
@@ -51,7 +70,11 @@ export function ChatMessages({ messages, isLoading, errorMessage }: ChatMessages
}
return (
<div className="flex-1 overflow-y-auto px-4 md:px-6">
<div
ref={scrollRef}
onScroll={updateShouldAutoScroll}
className="flex-1 overflow-y-auto px-4 md:px-6"
>
<div className="max-w-3xl mx-auto py-4 space-y-1">
{messages.map((message) => (
<MessageBubble key={message.id} message={message} />

View File

@@ -569,7 +569,7 @@ export function ChatPanel() {
]);
return (
<div className="flex flex-col h-full">
<div className="flex h-full min-h-0 flex-col overflow-hidden">
<ChatMessages messages={messages} isLoading={isLoading} errorMessage={chatError} />
<ChatInput
input={input}