diff --git a/packages/app/src/components/layout/AppLayout.tsx b/packages/app/src/components/layout/AppLayout.tsx index e995265a7..f2c8e6da9 100644 --- a/packages/app/src/components/layout/AppLayout.tsx +++ b/packages/app/src/components/layout/AppLayout.tsx @@ -150,6 +150,15 @@ export function AppLayout() { return () => window.removeEventListener("keydown", handleKeyDown, { capture: true }); }, [toggleCommandPalette]); + useEffect(() => { + const handleContextMenu = (event: MouseEvent) => { + event.preventDefault(); + }; + + window.addEventListener("contextmenu", handleContextMenu, { capture: true }); + return () => window.removeEventListener("contextmenu", handleContextMenu, { capture: true }); + }, []); + useEffect(() => { if (!isReaderActive) return; diff --git a/packages/app/src/components/reader/ReaderView.tsx b/packages/app/src/components/reader/ReaderView.tsx index 754f7cee5..74ab474db 100644 --- a/packages/app/src/components/reader/ReaderView.tsx +++ b/packages/app/src/components/reader/ReaderView.tsx @@ -2833,6 +2833,7 @@ export function ReaderView({ bookId, tabId }: ReaderViewProps) { (currentColor || defaultColor); const overlayRef = useRef(null); const popoverRef = useRef(null); + const closeTimerRef = useRef | null>(null); const [clampedPosition, setClampedPosition] = useState(position); + const clearPendingClose = () => { + if (!closeTimerRef.current) return; + clearTimeout(closeTimerRef.current); + closeTimerRef.current = null; + }; + + const selectionBounds = selectionRects.reduce((bounds, rect) => { + if (!bounds) { + return new DOMRect(rect.left, rect.top, rect.width, rect.height); + } + const left = Math.min(bounds.left, rect.left); + const top = Math.min(bounds.top, rect.top); + const right = Math.max(bounds.right, rect.right); + const bottom = Math.max(bounds.bottom, rect.bottom); + return new DOMRect(left, top, right - left, bottom - top); + }, null); + + const isPointInsideSelection = (x: number, y: number) => + !!selectionBounds && + x >= selectionBounds.left && + x <= selectionBounds.right && + y >= selectionBounds.top && + y <= selectionBounds.bottom; + const handleHighlightClick = () => { // PDF doesn't support highlighting if (isPdf) return; @@ -121,13 +148,31 @@ export function SelectionPopover({ ); }); + const handleBackdropClick = () => { + clearPendingClose(); + closeTimerRef.current = setTimeout(() => { + closeTimerRef.current = null; + onClose(); + }, 320); + }; + + const handleBackdropDoubleClick = (event: React.MouseEvent) => { + clearPendingClose(); + if (!isPointInsideSelection(event.clientX, event.clientY)) { + onClose(); + return; + } + onAskAI(); + }; + return (