Implement scrolling behavior in ChatArea and remove unused chat padding logic in ChatWindow

This commit is contained in:
Andrew Beal 2025-10-02 20:52:43 +01:00
parent 1bccc0139d
commit 94ad5e1e00
2 changed files with 39 additions and 20 deletions

View file

@ -4,17 +4,18 @@
import type { StreamingMarkdownService } from "Services/StreamingMarkdownService";
import ChatAreaThought from "./ChatAreaThought.svelte";
import StreamingIndicator from "./StreamingIndicator.svelte";
import { slide } from "svelte/transition";
import { Greeting } from "Enums/Greeting";
export let messages: Array<{id: string, content: string, isUser: boolean, isStreaming: boolean}> = [];
export let showChatPadding: boolean = false;
export let chatContainer: HTMLDivElement;
let streamingMarkdownService: StreamingMarkdownService = Resolve(Services.StreamingMarkdownService);
let messageElements = new Map<string, HTMLElement>();
let lastProcessedContent = new Map<string, string>();
let scrollInterval: number | null = null;
let userScrolledUp = false;
let lastScrollTop = 0;
function getGreetingByTime(): string {
const hour = new Date().getHours();
@ -42,9 +43,13 @@
messages.forEach((message) => {
if (!message.isUser) {
const lastContent = lastProcessedContent.get(message.id) || '';
// Only update if content has changed
if (message.content !== lastContent) {
if (message.isStreaming && lastContent === '') {
userScrolledUp = false;
}
updateMessageContent(message);
lastProcessedContent.set(message.id, message.content);
}
@ -52,6 +57,33 @@
});
}
function handleScroll() {
if (!chatContainer) return;
if (chatContainer.scrollTop < lastScrollTop) {
userScrolledUp = true;
}
lastScrollTop = chatContainer.scrollTop;
}
function startScrolling() {
if (scrollInterval || userScrolledUp) return;
scrollInterval = window.setInterval(() => {
if (chatContainer && !userScrolledUp) {
chatContainer.scrollTop = chatContainer.scrollHeight;
}
}, 50);
setTimeout(() => {
if (scrollInterval) {
clearInterval(scrollInterval);
scrollInterval = null;
}
}, 500);
}
function updateMessageContent(message: {id: string, content: string, isUser: boolean, isStreaming: boolean}) {
const element = messageElements.get(message.id);
if (!element) return;
@ -61,6 +93,7 @@
} else {
streamingMarkdownService.finalizeStream(message.id, message.content);
}
startScrolling();
}
function initializeMessageElement(messageId: string, element: HTMLElement) {
@ -112,7 +145,7 @@
}
</script>
<div class="chat-area" bind:this={chatContainer}>
<div class="chat-area" bind:this={chatContainer} on:scroll={handleScroll}>
{#each messages as message (message.id)}
<div class="message-container" class:user={message.isUser} class:assistant={!message.isUser}>
<div class="message-bubble" class:user={message.isUser} class:assistant={!message.isUser}>
@ -125,9 +158,6 @@
<div use:streamingAction={message.id} class="streaming-content"></div>
<StreamingIndicator/>
<ChatAreaThought/>
{#if showChatPadding}
<div class="chat-padding" transition:slide={{duration: 4000, delay: 0}}></div>
{/if}
{:else}
<!-- Static message: use traditional rendering -->
{@html getStaticHTML(message)}
@ -160,11 +190,6 @@
display: none;
}
.chat-padding {
height: 40vh;
width: 100%;
}
.message-container {
display: flex;
margin: 0;

View file

@ -18,8 +18,6 @@
let userRequest = "";
let isSubmitting = false;
let userHasScrolled = false;
let showChatPadding = false;
let messages: Array<{
id: string,
@ -54,9 +52,7 @@
await conversationService.saveConversation(messages);
showChatPadding = true;
scrollToBottom();
userHasScrolled = false;
try {
// Create AI message placeholder
@ -102,10 +98,8 @@
);
await conversationService.saveConversation(messages);
}
showChatPadding = false;
}
} finally {
showChatPadding = false;
semaphore.release();
isSubmitting = false;
}
@ -143,7 +137,7 @@
<main class="container">
<div id="chat-container">
<ChatArea bind:messages bind:chatContainer={chatContainer} bind:showChatPadding={showChatPadding}/>
<ChatArea bind:messages bind:chatContainer={chatContainer}/>
</div>
<div id="input-container">