From 564a3a3707f699f44505170bc12a7f60638d5429 Mon Sep 17 00:00:00 2001
From: Marco Beretta <81851188+berry-13@users.noreply.github.com>
Date: Fri, 31 Jul 2026 03:47:54 +0200
Subject: [PATCH] refactor(client): move web search answer box into a details
hover card
The inline answer card read as assistant prose sitting inside the
source list. The panel is back to sources only; an Info trigger at the
right edge of the row (hover-revealed like the chevron, focus-openable
for keyboard) opens a hover card with the search query, the source
count, and the provider answer box when present. WebSearch now
receives the tool call args to surface the query.
---
.../Chat/Messages/Content/WebSearch.tsx | 137 ++++++++++++------
.../Content/__tests__/WebSearch.test.tsx | 48 ++++--
client/src/locales/en/translation.json | 2 +
3 files changed, 134 insertions(+), 53 deletions(-)
diff --git a/client/src/components/Chat/Messages/Content/WebSearch.tsx b/client/src/components/Chat/Messages/Content/WebSearch.tsx
index a2f2be3082..d7df66a0bb 100644
--- a/client/src/components/Chat/Messages/Content/WebSearch.tsx
+++ b/client/src/components/Chat/Messages/Content/WebSearch.tsx
@@ -1,7 +1,8 @@
import { useMemo, useState, useEffect } from 'react';
import { useRecoilValue } from 'recoil';
import { Tools } from 'librechat-data-provider';
-import { Globe, ChevronDown } from 'lucide-react';
+import { Globe, ChevronDown, Info } from 'lucide-react';
+import { HoverCard, HoverCardTrigger, HoverCardPortal, HoverCardContent } from '@librechat/client';
import type {
TAttachment,
ValidSource,
@@ -13,6 +14,7 @@ import { disclosureChevronClassName, toolPanelSpacingClassName } from './disclos
import { FaviconImage, getCleanDomain } from '~/components/Web/SourceHovercard';
import { useLocalize, useExpandCollapse, useLazyCollapseBody } from '~/hooks';
import { StackedFavicons } from '~/components/Web/Sources';
+import parseJsonField from './Parts/parseJsonField';
import { useToolCallIntent } from './Parts/intent';
import { useSearchContext } from '~/Providers';
import { ROW_GLYPH_SLOT } from './rows';
@@ -225,6 +227,7 @@ export default function WebSearch({
const autoExpand = useRecoilValue(store.autoExpandTools);
const sourceCount = allSources.length;
+ const [showDetails, setShowDetails] = useState(false);
const [showSourceList, setShowSourceList] = useState(() => autoExpand && sourceCount > 0);
const { style: sourceExpandStyle, ref: sourceExpandRef } = useExpandCollapse(showSourceList);
const { shouldRenderBody, mountBody, handleTransitionEnd } = useLazyCollapseBody(showSourceList);
@@ -253,44 +256,104 @@ export default function WebSearch({
if (complete) {
const hasSourceData = sourceCount > 0;
const completedText = intent ?? localize('com_ui_web_searched');
+ const query = parseJsonField(args, 'query');
+ const hasDetails = !!query || !!answerBox;
return (
-
+
{completedText}
-