From 2ebe785ed5b523c53ae39c25cd8988ec5229bc89 Mon Sep 17 00:00:00 2001 From: Marco Beretta <81851188+berry-13@users.noreply.github.com> Date: Fri, 28 Aug 2026 20:54:51 +0200 Subject: [PATCH] fix(client): address fifth round of Codex review findings Compose the shared Button through HoverCardTrigger's asChild for the web search details trigger, which recreated rounding, hover colors, focus ring and motion locally. This also replaces Radix's default anchor with a real button. Only the hover-card reveal and the icon geometry stay local. Copy compact reasoning through copy-to-clipboard for the same reason as the handoff control: navigator.clipboard is undefined on a non-secure origin, where dereferencing it threw before anything was copied. Document the streaming-peek fade as a narrowly scoped custom-CSS exception. It is a mask-image stencil rather than paint, so only its alpha channel is read and no theme role could meaningfully restyle it. --- .../Chat/Messages/Content/Parts/Reasoning.tsx | 15 +++++++-- .../Chat/Messages/Content/WebSearch.tsx | 33 +++++++++++-------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/client/src/components/Chat/Messages/Content/Parts/Reasoning.tsx b/client/src/components/Chat/Messages/Content/Parts/Reasoning.tsx index dd22389266..9407e21632 100644 --- a/client/src/components/Chat/Messages/Content/Parts/Reasoning.tsx +++ b/client/src/components/Chat/Messages/Content/Parts/Reasoning.tsx @@ -1,4 +1,5 @@ import { memo, useMemo, useState, useEffect, useCallback, useRef, useId } from 'react'; +import copy from 'copy-to-clipboard'; import { useAtomValue } from 'jotai'; import { Lightbulb, ChevronDown } from 'lucide-react'; import { ContentTypes } from 'librechat-data-provider'; @@ -35,7 +36,15 @@ const lastSentences = (text: string): string => { /** Symmetric top + bottom edge fade so streaming text dissolves in at the * bottom and out at the top, framed by the same rounded outline the expanded - * panel uses. */ + * panel uses. + * + * Custom-CSS exception, narrowly scoped: this is a `mask-image` stencil, not + * paint. Only the alpha channel is read, so `#000` means "keep this pixel" + * and `transparent` means "hide it" — the hue never reaches the screen and no + * theme could meaningfully restyle it. Routing it through a theme role would + * invite a token with alpha, which would silently wash out the text the mask + * is supposed to keep. Tailwind has no mask-image utility that expresses a + * four-stop gradient with `calc()` offsets, hence the inline style. */ const PEEK_FADE = 'linear-gradient(to bottom, transparent, #000 1.25rem, #000 calc(100% - 1.25rem), transparent)'; @@ -292,7 +301,9 @@ export const ReasoningCompact = memo( }, []); const handleCopy = useCallback(() => { - navigator.clipboard.writeText(reasoningText); + if (!copy(reasoningText, { format: 'text/plain' })) { + return; + } setIsCopied(true); setTimeout(() => setIsCopied(false), 2000); }, [reasoningText]); diff --git a/client/src/components/Chat/Messages/Content/WebSearch.tsx b/client/src/components/Chat/Messages/Content/WebSearch.tsx index 1d4152efc5..0acfcb0fe7 100644 --- a/client/src/components/Chat/Messages/Content/WebSearch.tsx +++ b/client/src/components/Chat/Messages/Content/WebSearch.tsx @@ -3,6 +3,7 @@ import { useRecoilValue } from 'recoil'; import { Tools } from 'librechat-data-provider'; import { Globe, ChevronDown, Info } from 'lucide-react'; import { + Button, HoverCard, HoverCardTrigger, HoverCardPortal, @@ -306,19 +307,25 @@ export default function WebSearch({ {hasDetails && ( - setShowDetails(true)} - onBlur={() => setShowDetails(false)} - aria-label={localize('com_ui_web_search_details')} - > -