mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
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:
parent
09e8b11451
commit
73e180638e
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
30
frontend/src/hooks/useScrollToBottom.ts
Normal file
30
frontend/src/hooks/useScrollToBottom.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
@ -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": "选择要使用的语言模型",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user