summaryrefslogtreecommitdiff
path: root/extension/react-app/src/pages/gui.tsx
diff options
context:
space:
mode:
authorNate Sesti <sestinj@gmail.com>2023-09-04 23:34:04 -0700
committerNate Sesti <sestinj@gmail.com>2023-09-04 23:34:04 -0700
commite6d369f4312f0c8d175251e149c62d08608cb18c (patch)
treef03c42b77276d4ca3c7d11cffee306b8d88ffb68 /extension/react-app/src/pages/gui.tsx
parenta1328cb5431f99cfe16b246ee4201b19530404e2 (diff)
downloadsncontinue-e6d369f4312f0c8d175251e149c62d08608cb18c.tar.gz
sncontinue-e6d369f4312f0c8d175251e149c62d08608cb18c.tar.bz2
sncontinue-e6d369f4312f0c8d175251e149c62d08608cb18c.zip
feat: :sparkles: autoscrolling
Diffstat (limited to 'extension/react-app/src/pages/gui.tsx')
-rw-r--r--extension/react-app/src/pages/gui.tsx93
1 files changed, 66 insertions, 27 deletions
diff --git a/extension/react-app/src/pages/gui.tsx b/extension/react-app/src/pages/gui.tsx
index 60ad0c2c..3d98522a 100644
--- a/extension/react-app/src/pages/gui.tsx
+++ b/extension/react-app/src/pages/gui.tsx
@@ -99,24 +99,70 @@ function GUI(props: GUIProps) {
);
}, [bottomMessage, aboveComboBoxDivRef.current]);
- const [scrollTimeout, setScrollTimeout] = useState<NodeJS.Timeout | null>(
- null
+ const [isScrolling, setIsScrolling] = useState(false);
+
+ const [scrollTimeoutId, setScrollTimeoutId] = useState<NodeJS.Timeout>();
+
+ const scrollToBottom = useCallback(
+ (force: boolean = false) => {
+ if (scrollTimeoutId) {
+ return;
+ }
+
+ // Scroll only if user is within 100 pixels of the bottom of the window.
+ const edgeOffset = 50;
+ const scrollPosition = window.scrollY || 0;
+ const scrollHeight = topGuiDivRef.current?.scrollHeight || 0;
+ const clientHeight = window.innerHeight || 0;
+
+ const atBottomEdge =
+ scrollPosition + clientHeight + edgeOffset >= scrollHeight;
+
+ if (isScrolling || (!atBottomEdge && !force)) {
+ return;
+ }
+
+ window.scrollTo({
+ top: scrollHeight,
+ behavior: "smooth",
+ });
+
+ setScrollTimeoutId(
+ setTimeout(() => {
+ setScrollTimeoutId(undefined);
+ }, 800)
+ );
+ },
+ [
+ window.scrollY,
+ topGuiDivRef.current?.scrollHeight,
+ topGuiDivRef.current?.clientHeight,
+ isScrolling,
+ scrollTimeoutId,
+ ]
);
- const scrollToBottom = useCallback(() => {
- if (scrollTimeout) {
- clearTimeout(scrollTimeout);
- }
- // Debounced smooth scroll to bottom of screen
- if (topGuiDivRef.current) {
- const timeout = setTimeout(() => {
- window.scrollTo({
- top: topGuiDivRef.current!.offsetHeight,
- behavior: "smooth",
- });
- }, 200);
- setScrollTimeout(timeout);
- }
- }, [topGuiDivRef.current, scrollTimeout]);
+
+ const [timerId, setTimerId] = useState<NodeJS.Timeout>();
+
+ useEffect(() => {
+ const handleScrollEnd = () => {
+ setIsScrolling(false);
+ };
+
+ const handleScroll = () => {
+ window.clearTimeout(timerId);
+
+ setIsScrolling(true);
+
+ setTimerId(setTimeout(() => handleScrollEnd(), 800));
+ };
+
+ window.addEventListener("scroll", handleScroll);
+
+ return () => {
+ window.removeEventListener("scroll", handleScroll);
+ };
+ }, []);
useEffect(() => {
// Cmd + Backspace to delete current step
@@ -141,11 +187,6 @@ function GUI(props: GUIProps) {
useEffect(() => {
client?.onStateUpdate((state: FullState) => {
- // Scroll only if user is at very bottom of the window.
- const shouldScrollToBottom =
- topGuiDivRef.current &&
- topGuiDivRef.current?.offsetHeight - window.scrollY < 100;
-
const waitingForSteps =
state.active &&
state.history.current_index < state.history.timeline.length &&
@@ -177,14 +218,12 @@ function GUI(props: GUIProps) {
return nextStepsOpen;
});
- if (shouldScrollToBottom) {
- scrollToBottom();
- }
+ scrollToBottom();
});
}, [client]);
useEffect(() => {
- scrollToBottom();
+ scrollToBottom(true);
}, [waitingForSteps]);
// #endregion
@@ -339,7 +378,7 @@ function GUI(props: GUIProps) {
}, []);
return (
<div
- className="overflow-hidden"
+ className="overflow-scroll"
ref={topGuiDivRef}
onKeyDown={(e) => {
if (e.key === "Enter" && e.ctrlKey) {