import { useState, useMemo, memo, useCallback } from 'react'; import { useAtomValue } from 'jotai'; import { Lightbulb, ChevronDown } from 'lucide-react'; import { Clipboard, CheckMark } from '@librechat/client'; import type { MouseEvent, FC } from 'react'; import { showThinkingAtom } from '~/store/showThinking'; import { fontSizeAtom } from '~/store/fontSize'; import { useLocalize } from '~/hooks'; import { cn } from '~/utils'; /** * ThinkingContent - Displays the actual thinking/reasoning content * Used by both legacy text-based messages and modern content parts */ export const ThinkingContent: FC<{ children: React.ReactNode; }> = memo(({ children }) => { const fontSize = useAtomValue(fontSizeAtom); return (

{children}

); }); /** * ThinkingButton - Toggle button for expanding/collapsing thinking content * Shows lightbulb icon by default, chevron on hover * Shared between legacy Thinking component and modern ContentParts */ export const ThinkingButton = memo( ({ isExpanded, onClick, label, content, }: { isExpanded: boolean; onClick: (e: MouseEvent) => void; label: string; content?: string; }) => { const localize = useLocalize(); const fontSize = useAtomValue(fontSizeAtom); const [isCopied, setIsCopied] = useState(false); const handleCopy = useCallback( (e: MouseEvent) => { e.stopPropagation(); if (content) { navigator.clipboard.writeText(content); setIsCopied(true); setTimeout(() => setIsCopied(false), 2000); } }, [content], ); return (
{content && ( )}
); }, ); /** * Thinking Component (LEGACY SYSTEM) * * Used for simple text-based messages with `:::thinking:::` markers. * This handles the old message format where text contains embedded thinking blocks. * * Pattern: `:::thinking\n{content}\n:::\n{response}` * * Used by: * - MessageContent.tsx for plain text messages * - Legacy message format compatibility * - User messages when manually adding thinking content * * For modern structured content (agents/assistants), see Reasoning.tsx component. */ const Thinking: React.ElementType = memo(({ children }: { children: React.ReactNode }) => { const localize = useLocalize(); const showThinking = useAtomValue(showThinkingAtom); const [isExpanded, setIsExpanded] = useState(showThinking); const handleClick = useCallback((e: MouseEvent) => { e.preventDefault(); setIsExpanded((prev) => !prev); }, []); const label = useMemo(() => localize('com_ui_thoughts'), [localize]); // Extract text content for copy functionality const textContent = useMemo(() => { if (typeof children === 'string') { return children; } return ''; }, [children]); if (children == null) { return null; } return ( <>
{children}
); }); ThinkingButton.displayName = 'ThinkingButton'; ThinkingContent.displayName = 'ThinkingContent'; Thinking.displayName = 'Thinking'; export default memo(Thinking);