'use client'; import { useCallback, useEffect, useRef, useState, type MouseEvent as ReactMouseEvent } from 'react'; const DRAG_THRESHOLD_PX = 6; interface UseChatDragSelectionOptions { selectionMode: boolean; enterSelectionMode: (messageId: string) => void; addSelectedMessage: (messageId: string) => void; } const dragSuppressedUntilRef = { current: 0 }; export function suppressChatDragSelection(durationMs = 400) { dragSuppressedUntilRef.current = Date.now() + durationMs; } function isDragSuppressed() { return Date.now() < dragSuppressedUntilRef.current; } export function useChatDragSelection({ selectionMode, enterSelectionMode, addSelectedMessage }: UseChatDragSelectionOptions) { const [isDragging, setIsDragging] = useState(false); const visitedRef = useRef>(new Set()); const pendingDragRef = useRef<{ messageId: string; x: number; y: number } | null>(null); const startDragSelection = useCallback( (messageId: string) => { if (isDragSuppressed()) return; setIsDragging(true); visitedRef.current = new Set([messageId]); if (!selectionMode) { enterSelectionMode(messageId); } else { addSelectedMessage(messageId); } }, [addSelectedMessage, enterSelectionMode, selectionMode] ); const handleMessagePointerDown = useCallback( (messageId: string, event: ReactMouseEvent) => { if (event.button !== 0 || isDragSuppressed()) return; pendingDragRef.current = { messageId, x: event.clientX, y: event.clientY }; }, [] ); const handleDragEnterMessage = useCallback( (messageId: string) => { if (!isDragging || visitedRef.current.has(messageId)) return; visitedRef.current.add(messageId); addSelectedMessage(messageId); }, [addSelectedMessage, isDragging] ); const endDragSelection = useCallback(() => { pendingDragRef.current = null; setIsDragging(false); visitedRef.current.clear(); }, []); useEffect(() => { const onMouseMove = (event: MouseEvent) => { const pending = pendingDragRef.current; if (!pending || isDragSuppressed()) return; const distance = Math.hypot(event.clientX - pending.x, event.clientY - pending.y); if (distance < DRAG_THRESHOLD_PX) return; const messageId = pending.messageId; pendingDragRef.current = null; startDragSelection(messageId); }; window.addEventListener('mousemove', onMouseMove); window.addEventListener('mouseup', endDragSelection); return () => { window.removeEventListener('mousemove', onMouseMove); window.removeEventListener('mouseup', endDragSelection); }; }, [endDragSelection, startDragSelection]); return { isDragging, handleMessagePointerDown, handleDragEnterMessage, endDragSelection }; }