Skip to content

Commit 31c217f

Browse files
authored
🤖 perf: fix shimmer animation frame drops during compaction (#980)
Use CSS transform animation (compositor thread) instead of background-position (main thread repaint) to prevent frame drops when compaction summary is streaming. ## Changes - **CompactionBackground**: Use `translateX` transform instead of `background-position` - Add `shimmer-slide` keyframes to globals.css (runs on compositor thread) - Add `StreamingCompaction` story to visualize the effect ## Why this fixes frame drops CSS `transform` animations run on the GPU compositor thread, completely off the main JavaScript thread. This matters during streaming when the main thread is busy with React updates and text parsing. In contrast, `background-position` triggers repaints on every frame. _Generated with `mux`_
1 parent 4e3a1e5 commit 31c217f

File tree

4 files changed

+88
-13
lines changed

4 files changed

+88
-13
lines changed

src/browser/components/Messages/CompactionBackground.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,35 @@
11
import React from "react";
22

33
/**
4-
* Animated background for compaction streaming
5-
* Shimmer effect with moving gradient and particles for dynamic appearance
4+
* Animated background for compaction streaming.
5+
* Combines a subtle gradient with a GPU-accelerated shimmer effect.
6+
*
7+
* Uses CSS transform animation (compositor thread) instead of background-position
8+
* (main thread repaint) to avoid frame drops during heavy streaming work.
69
*/
7-
810
export const CompactionBackground: React.FC = () => {
911
return (
1012
<div className="pointer-events-none absolute inset-0 overflow-hidden rounded-md">
13+
{/* Subtle gradient background */}
1114
<div
12-
className="absolute inset-0 animate-[gradient-move_8s_ease_infinite] opacity-40"
15+
className="absolute inset-0 opacity-40"
1316
style={{
1417
background:
1518
"linear-gradient(-45deg, var(--color-plan-mode-alpha), color-mix(in srgb, var(--color-plan-mode) 30%, transparent), var(--color-plan-mode-alpha), color-mix(in srgb, var(--color-plan-mode) 25%, transparent))",
16-
backgroundSize: "400% 400%",
1719
}}
1820
/>
21+
{/* Shimmer uses CSS transform animation - runs on compositor thread, not main thread */}
22+
{/* Math: element is 300% wide, highlight at 50% = 150% from left edge.
23+
marginLeft -180% puts highlight at -30% (off-screen left).
24+
translateX 53.33% (of 300%) = 160%, moving highlight to 130% (off-screen right). */}
1925
<div
20-
className="absolute inset-0 animate-[shimmer_3s_infinite_linear]"
26+
className="absolute inset-0 animate-[shimmer-slide_3s_infinite_linear]"
27+
data-chromatic="ignore"
2128
style={{
2229
background:
2330
"linear-gradient(90deg, transparent 0%, transparent 40%, var(--color-plan-mode-alpha) 50%, transparent 60%, transparent 100%)",
24-
backgroundSize: "1000px 100%",
31+
width: "300%",
32+
marginLeft: "-180%",
2533
}}
2634
/>
2735
</div>

src/browser/stories/App.chat.stories.tsx

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
STABLE_TIMESTAMP,
88
createUserMessage,
99
createAssistantMessage,
10+
createCompactionRequestMessage,
1011
createFileReadTool,
1112
createFileEditTool,
1213
createTerminalTool,
@@ -325,3 +326,46 @@ export const GenericTool: AppStory = {
325326
});
326327
},
327328
};
329+
330+
/** Streaming compaction with shimmer effect - tests GPU-accelerated animation */
331+
export const StreamingCompaction: AppStory = {
332+
render: () => (
333+
<AppWithMocks
334+
setup={() =>
335+
setupStreamingChatStory({
336+
workspaceId: "ws-compaction",
337+
messages: [
338+
createUserMessage("msg-1", "Help me refactor this codebase", {
339+
historySequence: 1,
340+
timestamp: STABLE_TIMESTAMP - 300000,
341+
}),
342+
createAssistantMessage(
343+
"msg-2",
344+
"I've analyzed the codebase and made several improvements to the architecture.",
345+
{
346+
historySequence: 2,
347+
timestamp: STABLE_TIMESTAMP - 200000,
348+
}
349+
),
350+
createCompactionRequestMessage("msg-3", {
351+
historySequence: 3,
352+
timestamp: STABLE_TIMESTAMP - 3000,
353+
}),
354+
],
355+
streamingMessageId: "msg-4",
356+
historySequence: 4,
357+
streamText:
358+
"## Conversation Summary\n\nThe user requested help refactoring the codebase. Key changes made:\n\n- Restructured component hierarchy for better separation of concerns\n- Extracted shared utilities into dedicated modules\n- Improved type safety across API boundaries",
359+
})
360+
}
361+
/>
362+
),
363+
parameters: {
364+
docs: {
365+
description: {
366+
story:
367+
"Shows the compaction shimmer effect during streaming. The shimmer uses GPU-accelerated CSS transforms instead of background-position animations to prevent frame drops.",
368+
},
369+
},
370+
},
371+
};

src/browser/stories/mockFactory.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,29 @@ export function createUserMessage(
171171
};
172172
}
173173

174+
/** Create a compaction request user message (triggers shimmer effect on streaming response) */
175+
export function createCompactionRequestMessage(
176+
id: string,
177+
opts: { historySequence: number; timestamp?: number; rawCommand?: string }
178+
): ChatMuxMessage {
179+
const rawCommand = opts.rawCommand ?? "/compact";
180+
return {
181+
type: "message",
182+
id,
183+
role: "user",
184+
parts: [{ type: "text", text: rawCommand }],
185+
metadata: {
186+
historySequence: opts.historySequence,
187+
timestamp: opts.timestamp ?? STABLE_TIMESTAMP,
188+
muxMetadata: {
189+
type: "compaction-request",
190+
rawCommand,
191+
parsed: {},
192+
},
193+
},
194+
};
195+
}
196+
174197
export function createAssistantMessage(
175198
id: string,
176199
text: string,

src/browser/styles/globals.css

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,13 +1655,13 @@ pre code {
16551655
box-shadow: var(--thumb-shadow, none);
16561656
}
16571657

1658-
/* Custom animations for CompactionBackground */
1659-
@keyframes shimmer {
1660-
0% {
1661-
background-position: -1000px 0;
1658+
/* GPU-accelerated shimmer for CompactionBackground - runs on compositor thread */
1659+
@keyframes shimmer-slide {
1660+
from {
1661+
transform: translateX(0);
16621662
}
1663-
100% {
1664-
background-position: 1000px 0;
1663+
to {
1664+
transform: translateX(53.33%);
16651665
}
16661666
}
16671667

0 commit comments

Comments
 (0)