mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
fix: hide message actions after pointer clicks #642
Message action controls stay visible through focus-within for keyboard navigation, but pointer clicks left the copy/fork buttons focused after the mouse moved away. Release pointer focus on message actions so hover controls return to their quiet state while keeping Tab focus behavior intact. Constraint: Message actions use focus-within for keyboard accessibility and should not remove that path. Rejected: Remove focus-within reveal | would regress keyboard users who tab to copy or fork. Confidence: high Scope-risk: narrow Directive: Keep pointer-only blur scoped to message action controls; do not remove focus-within without a replacement keyboard affordance. Tested: cd desktop && bun run test src/components/chat/MessageList.test.tsx --run Tested: bun run check:desktop Not-tested: Live Tauri manual hover smoke Related: #642
This commit is contained in:
parent
7f74de711f
commit
fd25e5732f
@ -52,6 +52,7 @@ export function MessageActionBar({
|
||||
label={copyLabel}
|
||||
displayLabel={<Copy size={13} strokeWidth={2.2} aria-hidden="true" />}
|
||||
displayCopiedLabel={<Check size={13} strokeWidth={2.4} aria-hidden="true" />}
|
||||
onPointerUp={(event) => event.currentTarget.blur()}
|
||||
className="inline-flex h-7 w-7 items-center justify-center rounded-full border border-transparent bg-transparent text-[var(--color-text-tertiary)] transition-colors hover:border-[var(--color-brand)]/30 hover:bg-[var(--color-surface-container-low)] hover:text-[var(--color-text-primary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-brand)]/35"
|
||||
/>
|
||||
) : null}
|
||||
@ -62,6 +63,7 @@ export function MessageActionBar({
|
||||
disabled={branchAction.loading}
|
||||
aria-label={branchAction.label}
|
||||
title={branchAction.label}
|
||||
onPointerUp={(event) => event.currentTarget.blur()}
|
||||
className="inline-flex h-7 w-7 items-center justify-center rounded-full border border-transparent bg-transparent text-[var(--color-text-tertiary)] transition-colors hover:border-[var(--color-brand)]/30 hover:bg-[var(--color-surface-container-low)] hover:text-[var(--color-text-primary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-brand)]/35 disabled:cursor-wait disabled:opacity-60"
|
||||
>
|
||||
<GitFork size={13} strokeWidth={2.2} aria-hidden="true" />
|
||||
|
||||
@ -1580,6 +1580,33 @@ describe('MessageList nested tool calls', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('releases pointer focus from message actions after clicking copy', () => {
|
||||
useChatStore.setState({
|
||||
sessions: {
|
||||
[ACTIVE_TAB]: makeSessionState({
|
||||
messages: [
|
||||
{
|
||||
id: 'assistant-1',
|
||||
type: 'assistant_text',
|
||||
content: '离开 hover 后操作条应该恢复隐藏。',
|
||||
timestamp: 1,
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
||||
render(<MessageList />)
|
||||
|
||||
const copyButton = screen.getByRole('button', { name: 'Copy reply' })
|
||||
copyButton.focus()
|
||||
expect(document.activeElement).toBe(copyButton)
|
||||
|
||||
fireEvent.pointerUp(copyButton)
|
||||
|
||||
expect(document.activeElement).not.toBe(copyButton)
|
||||
})
|
||||
|
||||
it('adds selected user message text to the composer context', async () => {
|
||||
useChatStore.setState({
|
||||
sessions: {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { useEffect, useState, type ReactNode } from 'react'
|
||||
import { useEffect, useState, type PointerEventHandler, type ReactNode } from 'react'
|
||||
import { copyTextToClipboard } from '../chat/clipboard'
|
||||
|
||||
type Props = {
|
||||
@ -8,6 +8,7 @@ type Props = {
|
||||
displayLabel?: ReactNode
|
||||
displayCopiedLabel?: ReactNode
|
||||
className?: string
|
||||
onPointerUp?: PointerEventHandler<HTMLButtonElement>
|
||||
}
|
||||
|
||||
export function CopyButton({
|
||||
@ -17,6 +18,7 @@ export function CopyButton({
|
||||
displayLabel,
|
||||
displayCopiedLabel,
|
||||
className = '',
|
||||
onPointerUp,
|
||||
}: Props) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
@ -48,6 +50,7 @@ export function CopyButton({
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCopy}
|
||||
onPointerUp={onPointerUp}
|
||||
className={className}
|
||||
aria-label={currentLabel}
|
||||
title={currentLabel}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user