From 8240d9ce7f225b48e890239fc52fa26015e217db Mon Sep 17 00:00:00 2001 From: Danny Avila Date: Wed, 11 Mar 2026 23:02:37 -0400 Subject: [PATCH] refactor(Chat): enhance clipboard copy functionality and type definitions in Summary component Updated the Summary component to improve the clipboard copy functionality by handling clipboard permission errors. Refactored type definitions for SummaryProps to use a more specific type, enhancing type safety. Adjusted the SummaryButton and FloatingSummaryBar components to accept isCopied and onCopy props, promoting better separation of concerns and reusability. --- .../Chat/Messages/Content/Parts/Summary.tsx | 71 +++++++++++-------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/client/src/components/Chat/Messages/Content/Parts/Summary.tsx b/client/src/components/Chat/Messages/Content/Parts/Summary.tsx index 1d2b25f55f..44f4f302a8 100644 --- a/client/src/components/Chat/Messages/Content/Parts/Summary.tsx +++ b/client/src/components/Chat/Messages/Content/Parts/Summary.tsx @@ -3,11 +3,17 @@ import { useAtomValue } from 'jotai'; import { Clipboard, CheckMark, TooltipAnchor } from '@librechat/client'; import { ScrollText, ChevronDown, ChevronUp } from 'lucide-react'; import type { MouseEvent, FocusEvent } from 'react'; +import type { SummaryContentPart } from 'librechat-data-provider'; import { fontSizeAtom } from '~/store/fontSize'; import { useMessageContext } from '~/Providers'; import { useLocalize } from '~/hooks'; import { cn } from '~/utils'; +type SummaryProps = Pick< + SummaryContentPart, + 'content' | 'model' | 'provider' | 'tokenCount' | 'summarizing' +>; + function useCopyToClipboard(content?: string) { const [isCopied, setIsCopied] = useState(false); const timerRef = useRef>(); @@ -16,10 +22,16 @@ function useCopyToClipboard(content?: string) { (e: MouseEvent) => { e.stopPropagation(); if (content) { - navigator.clipboard.writeText(content); - clearTimeout(timerRef.current); - setIsCopied(true); - timerRef.current = setTimeout(() => setIsCopied(false), 2000); + navigator.clipboard.writeText(content).then( + () => { + clearTimeout(timerRef.current); + setIsCopied(true); + timerRef.current = setTimeout(() => setIsCopied(false), 2000); + }, + () => { + /* clipboard permission denied — leave icon unchanged */ + }, + ); } }, [content], @@ -27,16 +39,6 @@ function useCopyToClipboard(content?: string) { return { isCopied, handleCopy }; } -type ContentBlock = { type?: string; text?: string }; - -type SummaryProps = { - content?: ContentBlock[]; - model?: string; - provider?: string; - tokenCount?: number; - summarizing?: boolean; -}; - const SummaryContent = memo(({ children, meta }: { children: React.ReactNode; meta?: string }) => { const fontSize = useAtomValue(fontSizeAtom); @@ -56,6 +58,8 @@ const SummaryButton = memo( content, contentId, showCopyButton = true, + isCopied, + onCopy, }: { isExpanded: boolean; onClick: (e: MouseEvent) => void; @@ -63,10 +67,11 @@ const SummaryButton = memo( content?: string; contentId: string; showCopyButton?: boolean; + isCopied: boolean; + onCopy: (e: MouseEvent) => void; }) => { const localize = useLocalize(); const fontSize = useAtomValue(fontSizeAtom); - const { isCopied, handleCopy } = useCopyToClipboard(content); return (
@@ -98,7 +103,7 @@ const SummaryButton = memo( {content && showCopyButton && ( } /> @@ -188,7 +187,7 @@ const FloatingSummaryBar = memo(
{text}