feat: support for stopping automatic scrolling to bottom and add "to bottom" button (#1656)

Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com>
This commit is contained in:
Shimada666 2024-05-09 14:23:32 +08:00 committed by GitHub
parent 09e8b11451
commit 73e180638e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 76 additions and 16 deletions

View File

@ -1,10 +1,14 @@
import React, { useEffect, useRef } from "react";
import React, { useRef, useState } from "react";
import { useSelector } from "react-redux";
import SyntaxHighlighter from "react-syntax-highlighter";
import Markdown from "react-markdown";
import { atomOneDark } from "react-syntax-highlighter/dist/esm/styles/hljs";
import { VscArrowDown } from "react-icons/vsc";
import { useTranslation } from "react-i18next";
import { RootState } from "#/store";
import { Cell } from "#/state/jupyterSlice";
import { useScrollToBottom } from "#/hooks/useScrollToBottom";
import { I18nKey } from "#/i18n/declaration";
interface IJupyterCell {
cell: Cell;
@ -75,27 +79,49 @@ function JupyterCell({ cell }: IJupyterCell): JSX.Element {
}
function Jupyter(): JSX.Element {
const { t } = useTranslation();
const { cells } = useSelector((state: RootState) => state.jupyter);
const jupyterRef = useRef<HTMLDivElement>(null);
function scrollDomToBottom() {
const dom = jupyterRef.current;
if (dom) {
requestAnimationFrame(() => {
dom.scrollTo(0, dom.scrollHeight);
});
}
}
const [hitBottom, setHitBottom] = useState(true);
const { setAutoScroll, scrollDomToBottom } = useScrollToBottom(jupyterRef);
useEffect(() => {
scrollDomToBottom();
});
const onChatBodyScroll = (e: HTMLElement) => {
const bottomHeight = e.scrollTop + e.clientHeight;
const isHitBottom = bottomHeight >= e.scrollHeight - 10;
setHitBottom(isHitBottom);
setAutoScroll(isHitBottom);
};
return (
<div className="flex-1 overflow-y-auto flex flex-col" ref={jupyterRef}>
{cells.map((cell, index) => (
<JupyterCell key={index} cell={cell} />
))}
<div className="flex-1">
<div
className="overflow-y-auto h-full"
ref={jupyterRef}
onScroll={(e) => onChatBodyScroll(e.currentTarget)}
>
{cells.map((cell, index) => (
<JupyterCell key={index} cell={cell} />
))}
</div>
{!hitBottom && (
<div className="sticky bottom-2 flex items-center justify-center">
<button
type="button"
className="relative border-1 text-sm rounded px-3 py-1 border-neutral-600 bg-neutral-700 cursor-pointer select-none"
>
<span className="flex items-center" onClick={scrollDomToBottom}>
<VscArrowDown className="inline mr-2 w-3 h-3" />
<span className="inline-block" onClick={scrollDomToBottom}>
{t(I18nKey.CHAT_INTERFACE$TO_BOTTOM)}
</span>
</span>
</button>
</div>
)}
</div>
);
}

View File

@ -0,0 +1,30 @@
import { RefObject, useEffect, useState } from "react";
export function useScrollToBottom(scrollRef: RefObject<HTMLDivElement>) {
// for auto-scroll
const [autoScroll, setAutoScroll] = useState(true);
function scrollDomToBottom() {
const dom = scrollRef.current;
if (dom) {
requestAnimationFrame(() => {
setAutoScroll(true);
dom.scrollTo({ top: dom.scrollHeight, behavior: "auto" });
});
}
}
// auto scroll
useEffect(() => {
if (autoScroll) {
scrollDomToBottom();
}
});
return {
scrollRef,
autoScroll,
setAutoScroll,
scrollDomToBottom,
};
}

View File

@ -353,6 +353,10 @@
"fr": "Assistant",
"tr": "Gönder"
},
"CHAT_INTERFACE$TO_BOTTOM": {
"en": "To Bottom",
"zh-CN": "回到底部"
},
"SETTINGS$MODEL_TOOLTIP": {
"en": "Select the language model to use.",
"zh-CN": "选择要使用的语言模型",