import React, { useRef, useState, useMemo, useEffect, useCallback } from 'react'; import copy from 'copy-to-clipboard'; import { InfoIcon } from 'lucide-react'; import { Tools } from 'librechat-data-provider'; import { Clipboard, CheckMark } from '@librechat/client'; import type { CodeBarProps } from '~/common'; import ResultSwitcher from '~/components/Messages/Content/ResultSwitcher'; import { useToolCallsMapContext, useMessageContext } from '~/Providers'; import { LogContent } from '~/components/Chat/Messages/Content/Parts'; import RunCode from '~/components/Messages/Content/RunCode'; import { useLocalize } from '~/hooks'; import cn from '~/utils/cn'; type CodeBlockProps = Pick< CodeBarProps, 'lang' | 'plugin' | 'error' | 'allowExecution' | 'blockIndex' > & { codeChildren: React.ReactNode; classProp?: string; }; interface FloatingCodeBarProps extends CodeBarProps { isVisible: boolean; } const CodeBar: React.FC = React.memo( ({ lang, error, codeRef, blockIndex, plugin = null, allowExecution = true }) => { const localize = useLocalize(); const [isCopied, setIsCopied] = useState(false); return (
{lang} {plugin === true ? ( ) : (
{allowExecution === true && ( )}
)}
); }, ); const FloatingCodeBar: React.FC = React.memo( ({ lang, error, codeRef, blockIndex, plugin = null, allowExecution = true, isVisible }) => { const localize = useLocalize(); const [isCopied, setIsCopied] = useState(false); const copyButtonRef = useRef(null); const handleCopy = useCallback(() => { const codeString = codeRef.current?.textContent; if (codeString != null) { const wasFocused = document.activeElement === copyButtonRef.current; setIsCopied(true); copy(codeString.trim(), { format: 'text/plain' }); if (wasFocused) { requestAnimationFrame(() => { copyButtonRef.current?.focus(); }); } setTimeout(() => { const focusedElement = document.activeElement as HTMLElement | null; setIsCopied(false); requestAnimationFrame(() => { focusedElement?.focus(); }); }, 3000); } }, [codeRef]); return (
{plugin === true ? ( ) : ( <> {allowExecution === true && ( )} )}
); }, ); const CodeBlock: React.FC = ({ lang, blockIndex, codeChildren, classProp = '', allowExecution = true, plugin = null, error, }) => { const codeRef = useRef(null); const containerRef = useRef(null); const [isBarVisible, setIsBarVisible] = useState(false); const toolCallsMap = useToolCallsMapContext(); const { messageId, partIndex } = useMessageContext(); const key = allowExecution ? `${messageId}_${partIndex ?? 0}_${blockIndex ?? 0}_${Tools.execute_code}` : ''; const [currentIndex, setCurrentIndex] = useState(0); const fetchedToolCalls = toolCallsMap?.[key]; const [toolCalls, setToolCalls] = useState(toolCallsMap?.[key] ?? null); useEffect(() => { if (fetchedToolCalls) { setToolCalls(fetchedToolCalls); setCurrentIndex(fetchedToolCalls.length - 1); } }, [fetchedToolCalls]); // Handle focus within the container (for keyboard navigation) const handleFocus = useCallback(() => { setIsBarVisible(true); }, []); const handleBlur = useCallback((e: React.FocusEvent) => { // Check if focus is moving to another element within the container if (!containerRef.current?.contains(e.relatedTarget as Node)) { setIsBarVisible(false); } }, []); const handleMouseEnter = useCallback(() => { setIsBarVisible(true); }, []); const handleMouseLeave = useCallback(() => { // Only hide if no element inside has focus if (!containerRef.current?.contains(document.activeElement)) { setIsBarVisible(false); } }, []); const currentToolCall = useMemo(() => toolCalls?.[currentIndex], [toolCalls, currentIndex]); const next = () => { if (!toolCalls) { return; } if (currentIndex < toolCalls.length - 1) { setCurrentIndex(currentIndex + 1); } }; const previous = () => { if (currentIndex > 0) { setCurrentIndex(currentIndex - 1); } }; const isNonCode = !!(plugin === true || error === true); const language = isNonCode ? 'json' : lang; return (
{codeChildren}
{allowExecution === true && toolCalls && toolCalls.length > 0 && ( <>
                
              
{toolCalls.length > 1 && ( )} )}
); }; export default CodeBlock;