From aff8f628829ebe1cb09fc53d9ce53e0e2f8d6e8e Mon Sep 17 00:00:00 2001 From: Marco Beretta <81851188+berry-13@users.noreply.github.com> Date: Fri, 31 Jul 2026 03:29:14 +0200 Subject: [PATCH] feat(client): richer web search source list The panel only showed favicon, title, and domain while the search artifact carries much more. Source rows now include the result snippet (clamped to two lines) and date under the domain, and a Serper answer box renders as a lead card above the list with its title and snippet. Streaming behavior is unchanged; the answer box is attachment-only since it never carries citation anchors. --- .../Chat/Messages/Content/WebSearch.tsx | 62 +++++++++++++++++-- .../Content/__tests__/WebSearch.test.tsx | 33 ++++++++++ 2 files changed, 89 insertions(+), 6 deletions(-) diff --git a/client/src/components/Chat/Messages/Content/WebSearch.tsx b/client/src/components/Chat/Messages/Content/WebSearch.tsx index 9ba2647bed..a2f2be3082 100644 --- a/client/src/components/Chat/Messages/Content/WebSearch.tsx +++ b/client/src/components/Chat/Messages/Content/WebSearch.tsx @@ -7,6 +7,7 @@ import type { ValidSource, SearchResultData, PartMetadata, + AnswerBoxResult, } from 'librechat-data-provider'; import { disclosureChevronClassName, toolPanelSpacingClassName } from './disclosure'; import { FaviconImage, getCleanDomain } from '~/components/Web/SourceHovercard'; @@ -175,6 +176,22 @@ export default function WebSearch({ return []; }, [searchResults, attachments, ownTurn]); + /** Direct-answer card shown at the top of the source list. Attachment-only: + * answer boxes are provider extras (Serper) that never carry citations, so + * the streaming path does not need them. */ + const answerBox = useMemo((): AnswerBoxResult | undefined => { + if (!attachments) { + return undefined; + } + for (const att of attachments) { + const answer = att.type === Tools.web_search ? att[Tools.web_search]?.answerBox : undefined; + if (answer) { + return answer; + } + } + return undefined; + }, [attachments]); + // 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. @@ -284,8 +301,23 @@ export default function WebSearch({ 'mt-1.5 max-h-[280px] overflow-y-auto rounded-lg border border-border-light', )} > + {answerBox && (answerBox.title || answerBox.snippet) && ( +