mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-10-22 18:13:27 +00:00
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.
This commit is contained in:
parent
107841551e
commit
2ebe785ed5
2 changed files with 33 additions and 15 deletions
|
|
@ -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]);
|
||||
|
|
|
|||
|
|
@ -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({
|
|||
</button>
|
||||
{hasDetails && (
|
||||
<HoverCard openDelay={50} open={showDetails} onOpenChange={setShowDetails}>
|
||||
<HoverCardTrigger
|
||||
tabIndex={0}
|
||||
className={cn(
|
||||
'ml-auto inline-flex cursor-help items-center justify-center rounded-md p-1 text-text-secondary opacity-0 transition-all duration-200 ease-out',
|
||||
'hover:bg-surface-hover hover:text-text-primary',
|
||||
'group-focus-within/websearch:opacity-100 group-hover/websearch:opacity-100',
|
||||
'focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-heavy',
|
||||
)}
|
||||
onFocus={() => setShowDetails(true)}
|
||||
onBlur={() => setShowDetails(false)}
|
||||
aria-label={localize('com_ui_web_search_details')}
|
||||
>
|
||||
<Info className="size-3.5" aria-hidden="true" />
|
||||
{/* Composed through `asChild` so the shared Button owns the
|
||||
rounding, hover fill, focus ring and motion (and gives a real
|
||||
<button> instead of Radix's default anchor). Only the
|
||||
hover-card reveal and the icon geometry stay local. */}
|
||||
<HoverCardTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className={cn(
|
||||
'ml-auto size-auto cursor-help rounded-md p-1 text-text-secondary opacity-0',
|
||||
'group-focus-within/websearch:opacity-100 group-hover/websearch:opacity-100',
|
||||
'focus-visible:opacity-100',
|
||||
)}
|
||||
onFocus={() => setShowDetails(true)}
|
||||
onBlur={() => setShowDetails(false)}
|
||||
aria-label={localize('com_ui_web_search_details')}
|
||||
>
|
||||
<Info className="size-3.5" aria-hidden="true" />
|
||||
</Button>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardPortal>
|
||||
<HoverCardContent side="top" className="z-[999] w-80">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue