Skip to content

Commit 0d329dc

Browse files
committed
add scroll to bottom
1 parent a09d9e6 commit 0d329dc

File tree

3 files changed

+93
-41
lines changed

3 files changed

+93
-41
lines changed

apps/desktop/src/components/main/body/sessions/note-input/transcript/viewer/index.tsx

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,51 @@
11
import { cn } from "@hypr/utils";
2-
import { DependencyList, useLayoutEffect, useRef } from "react";
2+
import { DependencyList, useEffect, useLayoutEffect, useRef, useState } from "react";
3+
4+
import { useListener } from "../../../../../../../contexts/listener";
35
import { useSegments } from "./segment";
46

57
export function TranscriptViewer({ sessionId }: { sessionId: string }) {
68
const segments = useSegments(sessionId);
7-
return <Renderer segments={segments} />;
9+
return <Renderer segments={segments} sessionId={sessionId} />;
810
}
911

10-
function Renderer({ segments }: { segments: ReturnType<typeof useSegments> }) {
11-
const containerRef = useAutoScroll<HTMLDivElement>([segments]);
12+
function Renderer({ segments, sessionId }: { segments: ReturnType<typeof useSegments>; sessionId: string }) {
13+
const { containerRef, isAtBottom, scrollToBottom } = useScrollToBottom([segments]);
14+
const active = useListener((state) => state.status === "running_active" && state.sessionId === sessionId);
1215

1316
if (segments.length === 0) {
1417
return null;
1518
}
1619

1720
return (
18-
<div
19-
ref={containerRef}
20-
className={cn([
21-
"space-y-8 h-full overflow-y-auto overflow-x-hidden",
22-
"px-0.5 pb-32 scroll-pb-[8rem]",
23-
])}
24-
>
25-
{segments.map(
26-
(segment, i) => <Segment key={i} segment={segment} />,
21+
<div className="relative h-full">
22+
<div
23+
ref={containerRef}
24+
className={cn([
25+
"space-y-8 h-full overflow-y-auto overflow-x-hidden",
26+
"px-0.5 pb-16 scroll-pb-[8rem]",
27+
true ? "scrollbar-none" : "scroll-pb-[4rem]",
28+
])}
29+
>
30+
{segments.map(
31+
(segment, i) => <Segment key={i} segment={segment} />,
32+
)}
33+
</div>
34+
35+
{(!isAtBottom && active) && (
36+
<button
37+
onClick={scrollToBottom}
38+
className={cn([
39+
"absolute bottom-3 left-1/2 -translate-x-1/2",
40+
"px-4 py-2 rounded-full",
41+
"shadow-lg bg-neutral-800 hover:bg-neutral-700",
42+
"text-white text-xs font-light",
43+
"transition-all duration-200",
44+
"z-30",
45+
])}
46+
>
47+
Go to bottom
48+
</button>
2749
)}
2850
</div>
2951
);
@@ -69,6 +91,37 @@ function Segment({ segment }: { segment: ReturnType<typeof useSegments>[number]
6991
);
7092
}
7193

94+
function useScrollToBottom(deps: DependencyList) {
95+
const containerRef = useAutoScroll<HTMLDivElement>(deps);
96+
const [isAtBottom, setIsAtBottom] = useState(true);
97+
98+
useEffect(() => {
99+
const element = containerRef.current;
100+
if (!element) {
101+
return;
102+
}
103+
104+
const handleScroll = () => {
105+
const threshold = 100;
106+
const isNearBottom = element.scrollHeight - element.scrollTop - element.clientHeight < threshold;
107+
setIsAtBottom(isNearBottom);
108+
};
109+
110+
element.addEventListener("scroll", handleScroll);
111+
return () => element.removeEventListener("scroll", handleScroll);
112+
}, []);
113+
114+
const scrollToBottom = () => {
115+
const element = containerRef.current;
116+
if (!element) {
117+
return;
118+
}
119+
element.scrollTo({ top: element.scrollHeight, behavior: "smooth" });
120+
};
121+
122+
return { containerRef, isAtBottom, scrollToBottom };
123+
}
124+
72125
function useAutoScroll<T extends HTMLElement>(deps: DependencyList) {
73126
const ref = useRef<T | null>(null);
74127

apps/desktop/src/styles/globals.css

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@import url("https://fonts.googleapis.com/css2?family=Racing+Sans+One&display=swap");
2+
@import "./scrollbar.css";
23

34
@tailwind base;
45
@tailwind components;
@@ -79,34 +80,6 @@ body {
7980
overscroll-behavior: none;
8081
}
8182

82-
::-webkit-scrollbar {
83-
width: 8px;
84-
height: 8px;
85-
}
86-
87-
::-webkit-scrollbar-track {
88-
background: transparent;
89-
}
90-
91-
::-webkit-scrollbar-thumb {
92-
background: #e5e5e5;
93-
border-radius: 4px;
94-
}
95-
96-
::-webkit-scrollbar-thumb:hover {
97-
background: #d5d5d5;
98-
}
99-
100-
@layer utilities {
101-
.scrollbar-none {
102-
-ms-overflow-style: none;
103-
scrollbar-width: none;
104-
}
105-
.scrollbar-none::-webkit-scrollbar {
106-
display: none;
107-
}
108-
}
109-
11083
@layer base {
11184
* {
11285
@apply select-none;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
::-webkit-scrollbar {
2+
width: 6px;
3+
}
4+
5+
::-webkit-scrollbar-track {
6+
background: transparent;
7+
}
8+
9+
::-webkit-scrollbar-thumb {
10+
background: #e5e5e5;
11+
border-radius: 4px;
12+
}
13+
14+
::-webkit-scrollbar-thumb:hover {
15+
background: #d5d5d5;
16+
}
17+
18+
@layer utilities {
19+
.scrollbar-none {
20+
-ms-overflow-style: none;
21+
scrollbar-width: none;
22+
}
23+
.scrollbar-none::-webkit-scrollbar {
24+
display: none;
25+
}
26+
}

0 commit comments

Comments
 (0)