mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-02 21:19:12 +00:00
Only Auto Scroll when at Page Bottom and Add Button to Scroll to Page Bottom on Web App (#923)
Improve Scrolling on Chat page of Web app
- Details
1. Only auto scroll Khoj's streamed response when scroll is near bottom of page
Allows scrolling to other messages in conversation while Khoj is formulating and streaming its response
2. Add button to scroll to bottom of the chat page
3. Scroll to most recent conversation turn on conversation first load
It's a better default to anchor to most recent conversation turn (i.e most recent user message)
4. Smooth scroll when Khoj's chat response is streamed
Previously the scroll would jitter during response streaming
5. Anchor scroll position when fetch and render older messages in conversation
Allow users to keep their scroll position when older messages are fetched from server and rendered
Resolves #758
This commit is contained in:
@@ -4,7 +4,7 @@ import styles from "./chatMessage.module.css";
|
||||
|
||||
import markdownIt from "markdown-it";
|
||||
import mditHljs from "markdown-it-highlightjs";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import React, { useEffect, useRef, useState, forwardRef } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
|
||||
import "katex/dist/katex.min.css";
|
||||
@@ -275,7 +275,7 @@ export function TrainOfThought(props: TrainOfThoughtProps) {
|
||||
);
|
||||
}
|
||||
|
||||
export default function ChatMessage(props: ChatMessageProps) {
|
||||
const ChatMessage = forwardRef<HTMLDivElement, ChatMessageProps>((props, ref) => {
|
||||
const [copySuccess, setCopySuccess] = useState<boolean>(false);
|
||||
const [isHovering, setIsHovering] = useState<boolean>(false);
|
||||
const [textRendered, setTextRendered] = useState<string>("");
|
||||
@@ -406,10 +406,6 @@ export default function ChatMessage(props: ChatMessageProps) {
|
||||
}
|
||||
}, [markdownRendered, isHovering, messageRef]);
|
||||
|
||||
if (!props.chatMessage.message) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function formatDate(timestamp: string) {
|
||||
// Format date in HH:MM, DD MMM YYYY format
|
||||
let date = new Date(timestamp + "Z");
|
||||
@@ -449,6 +445,9 @@ export default function ChatMessage(props: ChatMessageProps) {
|
||||
function constructClasses(chatMessage: SingleChatMessage) {
|
||||
let classes = [styles.chatMessageContainer, "shadow-md"];
|
||||
classes.push(styles[chatMessage.by]);
|
||||
if (!chatMessage.message) {
|
||||
classes.push(styles.emptyChatMessage);
|
||||
}
|
||||
|
||||
if (props.customClassName) {
|
||||
classes.push(styles[`${chatMessage.by}${props.customClassName}`]);
|
||||
@@ -478,17 +477,8 @@ export default function ChatMessage(props: ChatMessageProps) {
|
||||
const sentenceRegex = /[^.!?]+[.!?]*/g;
|
||||
const chunks = props.chatMessage.message.match(sentenceRegex) || [];
|
||||
|
||||
if (!chunks) {
|
||||
return;
|
||||
}
|
||||
if (!chunks || chunks.length === 0 || !chunks[0]) return;
|
||||
|
||||
if (chunks.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!chunks[0]) {
|
||||
return;
|
||||
}
|
||||
setIsPlaying(true);
|
||||
|
||||
let nextBlobPromise = fetchBlob(chunks[0]);
|
||||
@@ -548,6 +538,7 @@ export default function ChatMessage(props: ChatMessageProps) {
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={constructClasses(props.chatMessage)}
|
||||
onMouseLeave={(event) => setIsHovering(false)}
|
||||
onMouseEnter={(event) => setIsHovering(true)}
|
||||
@@ -640,4 +631,8 @@ export default function ChatMessage(props: ChatMessageProps) {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
ChatMessage.displayName = "ChatMessage";
|
||||
|
||||
export default ChatMessage;
|
||||
|
||||
Reference in New Issue
Block a user