import { useState, useRef, useEffect, useCallback, type CSSProperties } from 'react'; import { createPortal } from 'react-dom'; import { Brain, X } from 'lucide-react'; import { useTranslation } from 'react-i18next'; import { thinkingModes } from '../../constants/thinkingModes'; type ThinkingModeSelectorProps = { selectedMode: string; onModeChange: (modeId: string) => void; onClose?: () => void; className?: string; }; function ThinkingModeSelector({ selectedMode, onModeChange, onClose, className = '' }: ThinkingModeSelectorProps) { const { t } = useTranslation('chat'); const [isOpen, setIsOpen] = useState(false); const containerRef = useRef(null); const triggerRef = useRef(null); const dropdownRef = useRef(null); const [dropdownStyle, setDropdownStyle] = useState(null); // Mapping from mode ID to translation key const modeKeyMap: Record = { 'think-hard': 'thinkHard', 'think-harder': 'thinkHarder' }; // Create translated modes for display const translatedModes = thinkingModes.map(mode => { const modeKey = modeKeyMap[mode.id] || mode.id; return { ...mode, name: t(`thinkingMode.modes.${modeKey}.name`), description: t(`thinkingMode.modes.${modeKey}.description`), prefix: t(`thinkingMode.modes.${modeKey}.prefix`) }; }); const closeDropdown = useCallback(() => { setIsOpen(false); onClose?.(); }, [onClose]); const updateDropdownPosition = useCallback(() => { const trigger = triggerRef.current; const dropdown = dropdownRef.current; if (!trigger || !dropdown || typeof window === 'undefined') { return; } const triggerRect = trigger.getBoundingClientRect(); const viewportPadding = window.innerWidth < 640 ? 12 : 16; const spacing = 8; const width = Math.min(window.innerWidth - viewportPadding * 2, window.innerWidth < 640 ? 320 : 256); let left = triggerRect.left + triggerRect.width / 2 - width / 2; left = Math.max(viewportPadding, Math.min(left, window.innerWidth - width - viewportPadding)); const measuredHeight = dropdown.offsetHeight || 0; const spaceBelow = window.innerHeight - triggerRect.bottom - spacing - viewportPadding; const spaceAbove = triggerRect.top - spacing - viewportPadding; const openBelow = spaceBelow >= Math.min(measuredHeight || 320, 320) || spaceBelow >= spaceAbove; const availableHeight = Math.min( window.innerHeight - viewportPadding * 2, Math.max(180, openBelow ? spaceBelow : spaceAbove), ); const panelHeight = Math.min(measuredHeight || availableHeight, availableHeight); const top = openBelow ? Math.min(triggerRect.bottom + spacing, window.innerHeight - viewportPadding - panelHeight) : Math.max(viewportPadding, triggerRect.top - spacing - panelHeight); setDropdownStyle({ position: 'fixed', top, left, width, maxHeight: availableHeight, zIndex: 80, }); }, []); useEffect(() => { if (!isOpen) { setDropdownStyle(null); return; } const rafId = window.requestAnimationFrame(updateDropdownPosition); const handleViewportChange = () => updateDropdownPosition(); window.addEventListener('resize', handleViewportChange); window.addEventListener('scroll', handleViewportChange, true); return () => { window.cancelAnimationFrame(rafId); window.removeEventListener('resize', handleViewportChange); window.removeEventListener('scroll', handleViewportChange, true); }; }, [isOpen, updateDropdownPosition]); useEffect(() => { if (!isOpen) { return; } const handlePointerDown = (event: PointerEvent) => { const target = event.target; if (!(target instanceof Node)) { return; } if (containerRef.current?.contains(target) || dropdownRef.current?.contains(target)) { return; } closeDropdown(); }; const handleKeyDown = (event: KeyboardEvent) => { if (event.key === 'Escape') { closeDropdown(); } }; document.addEventListener('pointerdown', handlePointerDown, true); document.addEventListener('keydown', handleKeyDown); return () => { document.removeEventListener('pointerdown', handlePointerDown, true); document.removeEventListener('keydown', handleKeyDown); }; }, [isOpen, closeDropdown]); const currentMode = translatedModes.find(mode => mode.id === selectedMode) || translatedModes[0]; const IconComponent = currentMode.icon || Brain; return (
{isOpen && typeof document !== 'undefined' && createPortal(

{t('thinkingMode.selector.title')}

{t('thinkingMode.selector.description')}

{translatedModes.map((mode) => { const ModeIcon = mode.icon; const isSelected = mode.id === selectedMode; return ( ); })}

Tip: {t('thinkingMode.selector.tip')}

, document.body )}
); } export default ThinkingModeSelector;