Skip to content

Commit 3b3a745

Browse files
committed
fix: reduce chat transcript pre-rendering
1 parent 69ef6a3 commit 3b3a745

2 files changed

Lines changed: 70 additions & 2 deletions

File tree

webview-ui/src/components/chat/ChatView.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ export interface ChatViewRef {
5656
}
5757

5858
export const MAX_IMAGES_PER_MESSAGE = 20 // This is the Anthropic limit.
59+
const CHAT_DEFAULT_ITEM_HEIGHT = 180
60+
const CHAT_VIEWPORT_BUFFER = {
61+
top: 600,
62+
bottom: 800,
63+
} as const
5964

6065
const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0
6166

@@ -1476,6 +1481,8 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
14761481
],
14771482
)
14781483

1484+
const computeMessageKey = useCallback((_index: number, messageOrGroup: ClineMessage) => messageOrGroup.ts, [])
1485+
14791486
// Function to handle mode switching
14801487
const switchToNextMode = useCallback(() => {
14811488
const allModes = getAllModes(customModes)
@@ -1635,7 +1642,9 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
16351642
ref={virtuosoRef}
16361643
key={task.ts}
16371644
className="scrollable grow overflow-y-scroll mb-1"
1638-
increaseViewportBy={{ top: 3_000, bottom: 1000 }}
1645+
computeItemKey={computeMessageKey}
1646+
defaultItemHeight={CHAT_DEFAULT_ITEM_HEIGHT}
1647+
increaseViewportBy={CHAT_VIEWPORT_BUFFER}
16391648
data={groupedMessages}
16401649
itemContent={itemContent}
16411650
followOutput={followOutputCallback}

webview-ui/src/components/chat/__tests__/ChatView.spec.tsx

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ import { vscode } from "@src/utils/vscode"
99

1010
import ChatView, { ChatViewProps } from "../ChatView"
1111

12+
const mockVirtuosoState = vi.hoisted(() => ({
13+
lastConfig: null as {
14+
computeItemKey?: (index: number, item: ClineMessage) => React.Key
15+
defaultItemHeight?: number
16+
increaseViewportBy?: number | { top?: number; bottom?: number }
17+
} | null,
18+
}))
19+
1220
// Define minimal types needed for testing
1321
interface ClineMessage {
1422
type: "say" | "ask"
@@ -61,14 +69,26 @@ vi.mock("react-virtuoso", () => ({
6169
Virtuoso: function MockVirtuoso({
6270
data,
6371
itemContent,
72+
computeItemKey,
73+
defaultItemHeight,
74+
increaseViewportBy,
6475
}: {
6576
data: ClineMessage[]
6677
itemContent: (index: number, item: ClineMessage) => React.ReactNode
78+
computeItemKey?: (index: number, item: ClineMessage) => React.Key
79+
defaultItemHeight?: number
80+
increaseViewportBy?: number | { top?: number; bottom?: number }
6781
}) {
82+
mockVirtuosoState.lastConfig = {
83+
computeItemKey,
84+
defaultItemHeight,
85+
increaseViewportBy,
86+
}
87+
6888
return (
6989
<div data-testid="virtuoso-item-list">
7090
{data.map((item, index) => (
71-
<div key={item.ts} data-testid={`virtuoso-item-${index}`}>
91+
<div key={computeItemKey?.(index, item) ?? item.ts} data-testid={`virtuoso-item-${index}`}>
7292
{itemContent(index, item)}
7393
</div>
7494
))}
@@ -454,6 +474,45 @@ describe("ChatView - Sound Playing Tests", () => {
454474
})
455475
})
456476

477+
describe("ChatView - Virtualization Configuration", () => {
478+
beforeEach(() => {
479+
vi.clearAllMocks()
480+
mockVirtuosoState.lastConfig = null
481+
})
482+
483+
it("keeps the off-screen render buffer tight for chat rows", async () => {
484+
renderChatView()
485+
486+
const taskTs = Date.now() - 100
487+
const rowTs = Date.now()
488+
489+
mockPostMessage({
490+
clineMessages: [
491+
{
492+
type: "say",
493+
say: "task",
494+
ts: taskTs,
495+
text: "Initial task",
496+
},
497+
{
498+
type: "say",
499+
say: "text",
500+
ts: rowTs,
501+
text: "Visible row",
502+
},
503+
],
504+
})
505+
506+
await waitFor(() => {
507+
expect(mockVirtuosoState.lastConfig).not.toBeNull()
508+
})
509+
510+
expect(mockVirtuosoState.lastConfig?.defaultItemHeight).toBe(180)
511+
expect(mockVirtuosoState.lastConfig?.increaseViewportBy).toEqual({ top: 600, bottom: 800 })
512+
expect(mockVirtuosoState.lastConfig?.computeItemKey?.(1, { type: "say", ts: rowTs })).toBe(rowTs)
513+
})
514+
})
515+
457516
describe("ChatView - Focus Grabbing Tests", () => {
458517
beforeEach(() => vi.clearAllMocks())
459518

0 commit comments

Comments
 (0)