Skip to content

Commit 692b1ef

Browse files
authored
get rid of virtualization (#263)
1 parent 361ab37 commit 692b1ef

File tree

2 files changed

+60
-102
lines changed

2 files changed

+60
-102
lines changed

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

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
isJsonRpcRequest,
1212
isJsonRpcResponse,
1313
} from "@shared/types/session-events";
14-
import { memo, useMemo } from "react";
14+
import { memo, useEffect, useMemo, useRef } from "react";
1515
import { GitActionMessage, parseGitActionMessage } from "./GitActionMessage";
1616
import { GitActionResult } from "./GitActionResult";
1717
import { SessionFooter } from "./SessionFooter";
@@ -20,7 +20,6 @@ import {
2020
SessionUpdateView,
2121
} from "./session-update/SessionUpdateView";
2222
import { UserMessage } from "./session-update/UserMessage";
23-
import { VirtualizedList } from "./VirtualizedList";
2423

2524
interface Turn {
2625
id: string;
@@ -46,32 +45,41 @@ export function ConversationView({
4645
repoPath,
4746
isCloud = false,
4847
}: ConversationViewProps) {
48+
const scrollRef = useRef<HTMLDivElement>(null);
4949
const turns = useMemo(() => buildTurns(events), [events]);
5050
const lastTurn = turns[turns.length - 1];
5151
const lastTurnComplete = lastTurn?.isComplete ?? true;
5252

53+
useEffect(() => {
54+
const el = scrollRef.current;
55+
if (el) {
56+
el.scrollTop = el.scrollHeight;
57+
}
58+
}, []);
59+
5360
return (
54-
<VirtualizedList
55-
items={turns}
56-
estimateSize={200}
57-
overscan={3}
58-
getItemKey={(turn) => turn.id}
59-
renderItem={(turn) => (
60-
<TurnView turn={turn} repoPath={repoPath} isCloud={isCloud} />
61-
)}
62-
autoScrollToBottom
63-
className="flex-1 bg-blackA-5 p-2 pb-16"
64-
gap={12}
65-
footer={
66-
<SessionFooter
67-
isPromptPending={isPromptPending || !lastTurnComplete}
68-
lastGenerationDuration={
69-
lastTurn?.isComplete ? lastTurn.durationMs : null
70-
}
71-
lastStopReason={lastTurn?.stopReason}
72-
/>
73-
}
74-
/>
61+
<div
62+
ref={scrollRef}
63+
className="scrollbar-hide flex-1 overflow-auto bg-blackA-5 p-2 pb-16"
64+
>
65+
<div className="flex flex-col gap-3">
66+
{turns.map((turn) => (
67+
<TurnView
68+
key={turn.id}
69+
turn={turn}
70+
repoPath={repoPath}
71+
isCloud={isCloud}
72+
/>
73+
))}
74+
</div>
75+
<SessionFooter
76+
isPromptPending={isPromptPending || !lastTurnComplete}
77+
lastGenerationDuration={
78+
lastTurn?.isComplete ? lastTurn.durationMs : null
79+
}
80+
lastStopReason={lastTurn?.stopReason}
81+
/>
82+
</div>
7583
);
7684
}
7785

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

Lines changed: 29 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useVirtualizer } from "@tanstack/react-virtual";
2-
import { type ReactNode, useEffect, useLayoutEffect, useRef } from "react";
2+
import { type ReactNode, useEffect, useRef } from "react";
33

44
interface VirtualizedListProps<T> {
55
items: T[];
@@ -25,8 +25,6 @@ export function VirtualizedList<T>({
2525
footer,
2626
}: VirtualizedListProps<T>) {
2727
const scrollRef = useRef<HTMLDivElement>(null);
28-
const isAtBottomRef = useRef(true);
29-
const isInitialMountRef = useRef(true);
3028

3129
const virtualizer = useVirtualizer({
3230
count: items.length,
@@ -40,91 +38,43 @@ export function VirtualizedList<T>({
4038
});
4139

4240
useEffect(() => {
43-
const el = scrollRef.current;
44-
if (!el) return;
45-
46-
let ticking = false;
47-
const handleScroll = () => {
48-
if (ticking) return;
49-
ticking = true;
50-
requestAnimationFrame(() => {
51-
const threshold = 50;
52-
isAtBottomRef.current =
53-
el.scrollHeight - el.scrollTop - el.clientHeight < threshold;
54-
ticking = false;
55-
});
56-
};
57-
58-
el.addEventListener("scroll", handleScroll, { passive: true });
59-
return () => el.removeEventListener("scroll", handleScroll);
60-
}, []);
61-
62-
useLayoutEffect(() => {
63-
const el = scrollRef.current;
64-
if (!el || !autoScrollToBottom || items.length === 0) {
65-
return;
66-
}
67-
68-
if (isInitialMountRef.current) {
69-
isInitialMountRef.current = false;
70-
el.scrollTop = el.scrollHeight;
71-
return;
72-
}
73-
}, [autoScrollToBottom, items.length]);
74-
75-
useLayoutEffect(() => {
76-
const el = scrollRef.current;
77-
if (
78-
!el ||
79-
!autoScrollToBottom ||
80-
items.length === 0 ||
81-
isInitialMountRef.current ||
82-
!isAtBottomRef.current
83-
) {
84-
return;
41+
if (autoScrollToBottom && items.length > 0) {
42+
virtualizer.scrollToIndex(items.length - 1, { align: "end" });
8543
}
86-
87-
el.scrollTop = el.scrollHeight;
88-
}, [autoScrollToBottom, items]);
44+
}, [autoScrollToBottom, items.length, virtualizer]);
8945

9046
const virtualItems = virtualizer.getVirtualItems();
9147

9248
return (
9349
<div
9450
ref={scrollRef}
9551
className={`${className} scrollbar-hide`}
96-
style={{
97-
height: "100%",
98-
overflow: "auto",
99-
scrollBehavior: "auto",
100-
}}
52+
style={{ height: "100%", overflow: "auto" }}
10153
>
102-
{items.length > 0 && (
103-
<div
104-
style={{
105-
height: virtualizer.getTotalSize(),
106-
width: "100%",
107-
position: "relative",
108-
}}
109-
>
110-
{virtualItems.map((virtualRow) => (
111-
<div
112-
key={virtualRow.key}
113-
ref={virtualizer.measureElement}
114-
data-index={virtualRow.index}
115-
style={{
116-
position: "absolute",
117-
top: 0,
118-
left: 0,
119-
width: "100%",
120-
transform: `translateY(${virtualRow.start}px)`,
121-
}}
122-
>
123-
{renderItem(items[virtualRow.index], virtualRow.index)}
124-
</div>
125-
))}
126-
</div>
127-
)}
54+
<div
55+
style={{
56+
height: virtualizer.getTotalSize(),
57+
width: "100%",
58+
position: "relative",
59+
}}
60+
>
61+
{virtualItems.map((virtualRow) => (
62+
<div
63+
key={virtualRow.key}
64+
ref={virtualizer.measureElement}
65+
data-index={virtualRow.index}
66+
style={{
67+
position: "absolute",
68+
top: 0,
69+
left: 0,
70+
width: "100%",
71+
transform: `translateY(${virtualRow.start}px)`,
72+
}}
73+
>
74+
{renderItem(items[virtualRow.index], virtualRow.index)}
75+
</div>
76+
))}
77+
</div>
12878
{footer}
12979
</div>
13080
);

0 commit comments

Comments
 (0)