From 1ae54b39ad8365cd14605e69db72b2ebc26ae96c Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Sun, 14 Jun 2026 13:57:23 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=8D=20fix:=20Render=20Web=20Search=20F?= =?UTF-8?q?avicons=20on=20Raw=20SERP=20Results=20During=20Streaming=20(#13?= =?UTF-8?q?741)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The streaming favicon stack was gated on `source.processed === true`, but the agents scrape pipeline marks sources processed only after a `Promise.all` barrier (the slowest page fetch). Raw SERP results — with everything needed to render favicons — arrive in the first attachment well before that, so the UI sat on "Searching the web" with no favicons for the entire scrape window. Render favicons from the raw sources as soon as they land instead of waiting for `processed`, filling the dead window and moving the label to "Processing results" immediately. Completed-state, turn scoping, and finalizing behavior are unchanged. --- .../Chat/Messages/Content/WebSearch.tsx | 21 +++++------- .../Content/__tests__/WebSearch.test.tsx | 32 ++++++++++++++----- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/client/src/components/Chat/Messages/Content/WebSearch.tsx b/client/src/components/Chat/Messages/Content/WebSearch.tsx index 7af0811fd7..c8d76a04b2 100644 --- a/client/src/components/Chat/Messages/Content/WebSearch.tsx +++ b/client/src/components/Chat/Messages/Content/WebSearch.tsx @@ -141,26 +141,21 @@ export default function WebSearch({ return []; }, [searchResults, attachments, ownTurn]); - const processedSources = useMemo(() => { + // Show favicons from the raw SERP results immediately rather than waiting for + // each source to flip to `processed`; the agents scrape barrier would otherwise + // freeze the stack on "Searching the web" for the slowest scrape's duration. + const streamingSources = useMemo(() => { if (complete && !finalizing) { return []; } - if (!searchResults) { - return []; - } - const result = searchResults[ownTurn]; + const result = searchResults?.[ownTurn]; if (!result) { return []; } - if (finalizing) { - return [...(result.organic || []), ...(result.topStories || [])]; - } - return [...(result.organic || []), ...(result.topStories || [])].filter( - (source) => source.processed === true, - ); + return [...(result.organic || []), ...(result.topStories || [])]; }, [searchResults, complete, finalizing, ownTurn]); - const showSources = processedSources.length > 0; + const showSources = streamingSources.length > 0; const progressText = useMemo(() => { let text: ProgressKeys = ownTurn !== '0' ? 'com_ui_web_searching_again' : 'com_ui_web_searching'; @@ -278,7 +273,7 @@ export default function WebSearch({ {progressText} - {showSources && } + {showSources && }