diff --git a/.jules/bolt.md b/.jules/bolt.md index edb687422..c50cfae98 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 50a0209c1..067fd672b 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 4b5db1852..18fe58b0f 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 + ); } /**