fix(desktop): show message Copy button on hover at bottom-right

Move the Copy button from always-visible below messages to the
bottom-right side of each message bubble, only appearing on hover.
Reduces visual clutter in chat conversations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
程序员阿江(Relakkes) 2026-04-09 22:04:05 +08:00
parent 41fc58d73a
commit 739b353a89
3 changed files with 24 additions and 26 deletions

View File

@ -8,12 +8,14 @@ type Props = {
export function AssistantMessage({ content, isStreaming }: Props) {
return (
<div className="mb-5 ml-10 max-w-[calc(100%-2.5rem)]">
<div className="rounded-[20px] rounded-tl-[8px] border border-[var(--color-border)]/60 bg-[var(--color-surface)] px-4 py-3 text-sm text-[var(--color-text-primary)] shadow-sm">
<MarkdownRenderer content={content} />
{isStreaming && (
<span className="ml-0.5 inline-block h-4 w-0.5 animate-shimmer bg-[var(--color-brand)] align-text-bottom" />
)}
<div className="group mb-5 ml-10 flex items-end gap-1.5">
<div className="min-w-0">
<div className="rounded-[20px] rounded-tl-[8px] border border-[var(--color-border)]/60 bg-[var(--color-surface)] px-4 py-3 text-sm text-[var(--color-text-primary)] shadow-sm">
<MarkdownRenderer content={content} />
{isStreaming && (
<span className="ml-0.5 inline-block h-4 w-0.5 animate-shimmer bg-[var(--color-brand)] align-text-bottom" />
)}
</div>
</div>
<MessageActionBar

View File

@ -3,22 +3,18 @@ import { CopyButton } from '../shared/CopyButton'
type Props = {
copyText?: string
copyLabel: string
align?: 'start' | 'end'
}
export function MessageActionBar({
copyText,
copyLabel,
align = 'start',
}: Props) {
const hasCopy = Boolean(copyText?.trim())
if (!hasCopy) return null
return (
<div
className={`mt-1.5 flex ${align === 'end' ? 'justify-end' : 'justify-start'}`}
>
<div className="shrink-0 pb-2 opacity-0 transition-opacity duration-200 group-hover:opacity-100">
<CopyButton
text={copyText!}
label={copyLabel}

View File

@ -11,28 +11,28 @@ export function UserMessage({ content, attachments }: Props) {
const hasText = content.trim().length > 0
return (
<div className="mb-5 flex justify-end">
<div className="max-w-[82%] space-y-2">
<div className="group mb-5 flex items-end justify-end gap-1.5">
<div className="min-w-0 max-w-[82%] space-y-2">
{attachments && attachments.length > 0 && (
<AttachmentGallery attachments={attachments} variant="message" />
)}
{hasText && (
<>
<div
className="bg-[var(--color-surface-user-msg)] px-4 py-3 text-sm leading-relaxed text-[var(--color-text-primary)] whitespace-pre-wrap break-words"
style={{ borderRadius: '18px 4px 18px 18px' }}
>
{content}
</div>
<MessageActionBar
copyText={content}
copyLabel="Copy prompt"
align="end"
/>
</>
<div
className="bg-[var(--color-surface-user-msg)] px-4 py-3 text-sm leading-relaxed text-[var(--color-text-primary)] whitespace-pre-wrap break-words"
style={{ borderRadius: '18px 4px 18px 18px' }}
>
{content}
</div>
)}
</div>
{hasText && (
<MessageActionBar
copyText={content}
copyLabel="Copy prompt"
/>
)}
</div>
)
}