mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-15 12:53:31 +08:00
fix: dismiss chat selection bubble on scroll (#672)
The chat add-to-session popover is anchored to viewport coordinates, so it can float over unrelated messages after the transcript scrolls. The shared selection-popover dismissal hook now treats captured scroll as a dismissal signal, matching the existing outside-click behavior and clearing the stale text selection.\n\nConstraint: Desktop selection popovers are owned by message/workspace child components, not by the transcript scroll container.\nRejected: Thread scroll state through every selectable message | would re-render message rows during scroll for a global dismissal concern.\nConfidence: high\nScope-risk: narrow\nDirective: Keep selection popover dismissal centralized unless a future menu needs scroll persistence explicitly.\nTested: bun run test -- src/components/chat/MessageList.test.tsx -t "dismisses the selected-message action when the message list scrolls"\nTested: bun run test -- src/components/chat/MessageList.test.tsx\nTested: bun run check:desktop
This commit is contained in:
parent
80b2e98f8d
commit
e9e4f3e1e5
@ -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(<MessageList />)
|
||||
|
||||
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: {
|
||||
|
||||
@ -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])
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user