From a98fee4abd8240cf9132f5309379da1db987699d Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 29 Oct 2026 06:52:35 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20chat=20signature?= =?UTF-8?q?s=20by=20replacing=20array=20joins=20with=20string=20concatenat?= =?UTF-8?q?ion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces `Array.prototype.join()` with direct string concatenation in chat signature generation loops (`messagesSignature` and `useStickToBottom`). This avoids unnecessary intermediate array allocations, reducing memory overhead and garbage collection pauses on hot paths during frequent polling. Co-authored-by: bobdivx <6737167+bobdivx@users.noreply.github.com> --- .jules/bolt.md | 4 ++++ apps/web/src/components/chat/ChatThread.tsx | 26 +++++++++++++-------- apps/web/src/lib/chat-messages.ts | 18 ++++++++------ 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index edb6874221..c50cfae982 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -5,3 +5,7 @@ ## 2026-10-15 - [Performance] Prevent unnecessary UI array recalculations **Learning:** In the chat component (`useThreadList`), derived state that requires filtering and sorting large arrays (like generating nested agent lists) was running synchronously on every render. Given that chat polling triggers frequent re-renders, this caused unnecessary CPU strain on lists that only rarely change. **Action:** Use `useMemo` for derived states like `rows` and `current` in custom hooks when they involve `.filter()`, `.map()`, or `.sort()` and depend on data that updates less frequently than the component renders. + +## 2026-10-29 - [Performance] Optimize Chat Signature Generation Hot Paths +**Learning:** In chat signature generation functions (like `messagesSignature` and `useStickToBottom`), array instantiation combined with `.join('|')` causes unnecessary memory allocation overhead during frequent polling or render cycles. +**Action:** Replace `Array.prototype.join()` with direct string concatenation on hot paths where signatures are generated frequently. diff --git a/apps/web/src/components/chat/ChatThread.tsx b/apps/web/src/components/chat/ChatThread.tsx index 50a0209c1b..067fd672b2 100644 --- a/apps/web/src/components/chat/ChatThread.tsx +++ b/apps/web/src/components/chat/ChatThread.tsx @@ -18,16 +18,22 @@ export function useStickToBottom(messages: ChatMessage[], resetKey: unknown) { if (!box) return; // On ne défile que si le contenu a réellement changé. const last = messages[messages.length - 1]; - const sig = [ - messages.length, - last?.content.length ?? 0, - last?.toolCalls?.length ?? 0, - last?.actions?.length ?? 0, - last?.reflections?.length ?? 0, - last?.milestones?.length ?? 0, - last?.liveLabel ?? '', - last?.streaming ? 1 : 0, - ].join('|'); + const sig = + messages.length + + '|' + + (last?.content.length ?? 0) + + '|' + + (last?.toolCalls?.length ?? 0) + + '|' + + (last?.actions?.length ?? 0) + + '|' + + (last?.reflections?.length ?? 0) + + '|' + + (last?.milestones?.length ?? 0) + + '|' + + (last?.liveLabel ?? '') + + '|' + + (last?.streaming ? 1 : 0); if (sig === lastSig.current) return; const first = lastSig.current === ''; lastSig.current = sig; diff --git a/apps/web/src/lib/chat-messages.ts b/apps/web/src/lib/chat-messages.ts index 4b5db18523..18fe58b0fb 100644 --- a/apps/web/src/lib/chat-messages.ts +++ b/apps/web/src/lib/chat-messages.ts @@ -50,13 +50,17 @@ export function messagesSignature(messages: IdentifiedMessage[]): string { } } - return [ - messages.length, - last?.id ?? '', - last?.content.length ?? 0, - last?.toolCalls?.length ?? 0, - reactionsSig, - ].join('|'); + return ( + messages.length + + '|' + + (last?.id ?? '') + + '|' + + (last?.content.length ?? 0) + + '|' + + (last?.toolCalls?.length ?? 0) + + '|' + + reactionsSig + ); } /**