Skip to content

Commit 06809f1

Browse files
committed
fix: memory leak cleanups — copy timeout, batch timer, void animation
1 parent 7d7b341 commit 06809f1

3 files changed

Lines changed: 22 additions & 3 deletions

File tree

app/components/chat/Chat.client.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ export const ChatImpl = memo(
496496
logger.error('Failed to clear search params:', e);
497497
}
498498

499-
runAnimation();
499+
void runAnimation();
500500
append({
501501
role: 'user',
502502
content: `[Model: ${modelRef.current}]\n\n[Provider: ${providerRef.current.name}]\n\n${urlPrompt}`,
@@ -997,7 +997,7 @@ export const ChatImpl = memo(
997997
// Switch from plan mode to build mode — planning is done, execution begins
998998
setPlanMode(false);
999999

1000-
runAnimation();
1000+
void runAnimation();
10011001

10021002
append({
10031003
role: 'user',

app/components/chat/ToolInvocations.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ const FormattedResultContent = memo(({ text, theme }: { text: string; theme: The
332332
const ToolResultItem = memo(({ tool, annotation, theme }: ToolResultItemProps) => {
333333
const [isExpanded, setIsExpanded] = useState(false);
334334
const [copied, setCopied] = useState(false);
335+
const copyTimerRef = useRef<ReturnType<typeof setTimeout>>(null);
335336
const resultContainerRef = useRef<HTMLDivElement>(null);
336337
const [isOverflowing, setIsOverflowing] = useState(false);
337338

@@ -367,6 +368,14 @@ const ToolResultItem = memo(({ tool, annotation, theme }: ToolResultItemProps) =
367368
const isLongResult = lineCount > RESULT_LINE_THRESHOLD;
368369
const isFormattedLong = (extracted.text?.split('\n').length ?? 0) > RESULT_LINE_THRESHOLD;
369370

371+
useEffect(() => {
372+
return () => {
373+
if (copyTimerRef.current) {
374+
clearTimeout(copyTimerRef.current);
375+
}
376+
};
377+
}, []);
378+
370379
// Detect whether the result container overflows the collapsed max-height
371380
useEffect(() => {
372381
if (resultContainerRef.current) {
@@ -393,7 +402,12 @@ const ToolResultItem = memo(({ tool, annotation, theme }: ToolResultItemProps) =
393402
const textToCopy = viewMode === 'formatted' && extracted.text ? extracted.text : resultStr;
394403
await navigator.clipboard.writeText(textToCopy);
395404
setCopied(true);
396-
setTimeout(() => setCopied(false), 2000);
405+
406+
if (copyTimerRef.current) {
407+
clearTimeout(copyTimerRef.current);
408+
}
409+
410+
copyTimerRef.current = setTimeout(() => setCopied(false), 2000);
397411
} catch {
398412
logger.error('Failed to copy result to clipboard');
399413
}

app/lib/stores/stream-event-router.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,11 @@ async function persistCheckpointFromEvent(event: AgentCheckpointEvent): Promise<
351351
* Called from Chat.client.tsx onFinish to clear stale phase badges.
352352
*/
353353
export function resetStreamEventState(): void {
354+
if (parallelBatchClearTimer !== null) {
355+
clearTimeout(parallelBatchClearTimer);
356+
parallelBatchClearTimer = null;
357+
}
358+
354359
latestPlanPhaseChange.set(null);
355360
latestReviewCycle.set(null);
356361
structuredStreamingActive.set(false);

0 commit comments

Comments
 (0)