Skip to content

Commit 4aad4e4

Browse files
committed
Create auto scroll button
1 parent 472b3d0 commit 4aad4e4

File tree

1 file changed

+57
-29
lines changed

1 file changed

+57
-29
lines changed

apps/array/src/renderer/features/sessions/components/ConversationView.tsx

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,23 @@ import type {
33
SessionNotification,
44
} from "@agentclientprotocol/sdk";
55
import type { SessionUpdate, ToolCall } from "@features/sessions/types";
6-
import { XCircle } from "@phosphor-icons/react";
7-
import { Box, Flex, Text } from "@radix-ui/themes";
6+
import { ArrowDown, XCircle } from "@phosphor-icons/react";
7+
import { Box, Button, Flex, Text } from "@radix-ui/themes";
88
import {
99
type AcpMessage,
1010
isJsonRpcNotification,
1111
isJsonRpcRequest,
1212
isJsonRpcResponse,
1313
type UserShellExecuteParams,
1414
} from "@shared/types/session-events";
15-
import { memo, useLayoutEffect, useMemo, useRef } from "react";
15+
import {
16+
memo,
17+
useCallback,
18+
useLayoutEffect,
19+
useMemo,
20+
useRef,
21+
useState,
22+
} from "react";
1623
import { GitActionMessage, parseGitActionMessage } from "./GitActionMessage";
1724
import { GitActionResult } from "./GitActionResult";
1825
import { SessionFooter } from "./SessionFooter";
@@ -47,6 +54,9 @@ interface ConversationViewProps {
4754
isCloud?: boolean;
4855
}
4956

57+
const SCROLL_THRESHOLD = 100;
58+
const SHOW_BUTTON_THRESHOLD = 300;
59+
5060
export function ConversationView({
5161
events,
5262
isPromptPending,
@@ -59,17 +69,18 @@ export function ConversationView({
5969

6070
const isNearBottomRef = useRef(true);
6171
const prevItemsLengthRef = useRef(0);
72+
const [showScrollButton, setShowScrollButton] = useState(false);
6273

6374
// Update isNearBottom on scroll
6475
useLayoutEffect(() => {
6576
const el = scrollRef.current;
6677
if (!el) return;
6778

6879
const handleScroll = () => {
69-
const threshold = 100;
7080
const distanceFromBottom =
7181
el.scrollHeight - el.scrollTop - el.clientHeight;
72-
isNearBottomRef.current = distanceFromBottom <= threshold;
82+
isNearBottomRef.current = distanceFromBottom <= SCROLL_THRESHOLD;
83+
setShowScrollButton(distanceFromBottom > SHOW_BUTTON_THRESHOLD);
7384
};
7485

7586
el.addEventListener("scroll", handleScroll);
@@ -89,32 +100,49 @@ export function ConversationView({
89100
}
90101
}, [items]);
91102

103+
const scrollToBottom = useCallback(() => {
104+
const el = scrollRef.current;
105+
if (el) {
106+
el.scrollTo({ top: el.scrollHeight, behavior: "smooth" });
107+
}
108+
}, []);
109+
92110
return (
93-
<div
94-
ref={scrollRef}
95-
className="scrollbar-hide flex-1 overflow-auto bg-white p-2 pb-16 dark:bg-gray-1"
96-
>
97-
<div className="flex flex-col gap-3">
98-
{items.map((item) =>
99-
item.type === "turn" ? (
100-
<TurnView
101-
key={item.id}
102-
turn={item}
103-
repoPath={repoPath}
104-
isCloud={isCloud}
105-
/>
106-
) : (
107-
<UserShellExecuteView key={item.id} item={item} />
108-
),
109-
)}
111+
<div className="relative flex-1">
112+
<div
113+
ref={scrollRef}
114+
className="scrollbar-hide absolute inset-0 overflow-auto bg-white p-2 pb-16 dark:bg-gray-1"
115+
>
116+
<div className="flex flex-col gap-3">
117+
{items.map((item) =>
118+
item.type === "turn" ? (
119+
<TurnView
120+
key={item.id}
121+
turn={item}
122+
repoPath={repoPath}
123+
isCloud={isCloud}
124+
/>
125+
) : (
126+
<UserShellExecuteView key={item.id} item={item} />
127+
),
128+
)}
129+
</div>
130+
<SessionFooter
131+
isPromptPending={isPromptPending}
132+
lastGenerationDuration={
133+
lastTurn?.isComplete ? lastTurn.durationMs : null
134+
}
135+
lastStopReason={lastTurn?.stopReason}
136+
/>
110137
</div>
111-
<SessionFooter
112-
isPromptPending={isPromptPending}
113-
lastGenerationDuration={
114-
lastTurn?.isComplete ? lastTurn.durationMs : null
115-
}
116-
lastStopReason={lastTurn?.stopReason}
117-
/>
138+
{showScrollButton && (
139+
<Box className="absolute right-4 bottom-4 z-10">
140+
<Button size="1" variant="solid" onClick={scrollToBottom}>
141+
<ArrowDown size={14} weight="bold" />
142+
Scroll to bottom
143+
</Button>
144+
</Box>
145+
)}
118146
</div>
119147
);
120148
}

0 commit comments

Comments
 (0)