From 47cae9f71342a62bb13d86a11dc266017674dd21 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 3 Nov 2026 07:12:52 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20[performance=20improvement]?= =?UTF-8?q?=20CodeBlock=20string=20check=20useMemo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 💡 What: Wrapped the highlighted tokens check against the raw string code with `useMemo`, utilizing a manual `for` loop for string concatenation instead of nested `.map().join()`. 🎯 Why: `CodeBlock` component derived state caused an expensive nested `.map().join()` execution on every render checking whether tokens matched the source code. 📊 Impact: Prevents CPU/Memory overhead incurred by allocations for intermediate arrays during frequent component renders. 🔬 Measurement: Verify via React Profiler that `CodeBlock` components are not recalculating string concatenations dynamically on every render round. Co-authored-by: bobdivx <6737167+bobdivx@users.noreply.github.com> --- .jules/bolt.md | 4 ++++ apps/web/src/components/chat/CodeBlock.tsx | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index edb6874221..907e05762a 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-11-03 - [Performance] Memoize syntax-highlighting check in CodeBlock +**Learning:** Checking highlighted tokens against original source code was unnecessarily recalculating string concatenations dynamically on every render round in `CodeBlock.tsx`. +**Action:** Utilize a `useMemo` block encapsulating manual string concatenation array traversal via nested `for` loop (as `map().join()` incurs expensive memory allocations during intermediate arrays creation). diff --git a/apps/web/src/components/chat/CodeBlock.tsx b/apps/web/src/components/chat/CodeBlock.tsx index a86bfe9584..894dca504e 100644 --- a/apps/web/src/components/chat/CodeBlock.tsx +++ b/apps/web/src/components/chat/CodeBlock.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'preact/hooks'; +import { useEffect, useRef, useState, useMemo } from 'preact/hooks'; import { Check, Copy } from 'lucide-preact'; import { copyText } from '../../lib/clipboard'; import { highlightCode, normalizeLang, type CodeToken } from '../../lib/highlight'; @@ -26,7 +26,19 @@ export function CodeBlock({ code, lang }: { code: string; lang?: string }) { window.clearTimeout(t); }; }, [code, lang]); - const shown = tokens && tokens.map((l) => l.map((t) => t.content).join('')).join('\n') === code ? tokens : null; + + const shown = useMemo(() => { + if (!tokens) return null; + let expected = ""; + for (let i = 0; i < tokens.length; i++) { + const line = tokens[i]; + for (let j = 0; j < line.length; j++) { + expected += line[j].content; + } + if (i < tokens.length - 1) expected += '\n'; + } + return expected === code ? tokens : null; + }, [tokens, code]); async function onCopy() { const ok = await copyText(code);