diff --git a/desktop/src/components/chat/MessageList.test.tsx b/desktop/src/components/chat/MessageList.test.tsx index 5823c4fc..da15625e 100644 --- a/desktop/src/components/chat/MessageList.test.tsx +++ b/desktop/src/components/chat/MessageList.test.tsx @@ -1715,6 +1715,36 @@ describe('MessageList nested tool calls', () => { expect(window.getSelection()?.toString()).toBe('') }) + it('dismisses the selected-message action when the message list scrolls', async () => { + useChatStore.setState({ + sessions: { + [ACTIVE_TAB]: makeSessionState({ + messages: [{ + id: 'assistant-1', + type: 'assistant_text', + content: 'Scrolling should clear this selected reply.', + timestamp: 1, + }], + }), + }, + }) + + const { container } = render() + + const assistantText = screen.getByText(/Scrolling should clear/) + await selectMessageText(assistantText, 'selected reply') + expect(screen.getByRole('button', { name: 'Add to chat' })).toBeTruthy() + + const scroller = container.querySelector('.overflow-y-auto') as HTMLDivElement + await act(async () => { + fireEvent.scroll(scroller) + await Promise.resolve() + }) + + expect(screen.queryByRole('button', { name: 'Add to chat' })).toBeNull() + expect(window.getSelection()?.toString()).toBe('') + }) + it('keeps only the latest selected-message action when selecting across messages', async () => { useChatStore.setState({ sessions: { diff --git a/desktop/src/hooks/useSelectionPopoverDismiss.ts b/desktop/src/hooks/useSelectionPopoverDismiss.ts index a7c17acf..505efe42 100644 --- a/desktop/src/hooks/useSelectionPopoverDismiss.ts +++ b/desktop/src/hooks/useSelectionPopoverDismiss.ts @@ -76,6 +76,11 @@ export function useSelectionPopoverDismiss({ useEffect(() => { if (!active) return + const dismiss = () => { + onDismiss() + clearWindowSelection() + } + const handlePointerDown = (event: PointerEvent) => { const popover = popoverRef.current const target = event.target @@ -83,11 +88,18 @@ export function useSelectionPopoverDismiss({ return } - onDismiss() - clearWindowSelection() + dismiss() + } + + const handleScroll = () => { + dismiss() } document.addEventListener('pointerdown', handlePointerDown, true) - return () => document.removeEventListener('pointerdown', handlePointerDown, true) + document.addEventListener('scroll', handleScroll, true) + return () => { + document.removeEventListener('pointerdown', handlePointerDown, true) + document.removeEventListener('scroll', handleScroll, true) + } }, [active, onDismiss, popoverRef]) }