cc-haha/desktop/src/components/chat/UserMessage.tsx
程序员阿江(Relakkes) 70fb6c429e Keep desktop transcript and plugin controls readable after layout regressions
Two desktop surfaces regressed in ways that made the app harder to read and operate.
The plugin header compressed summary cards and action buttons too early for the available width,
and the rewind/copied affordances in chat were sharing the same horizontal lane as message bubbles,
which visually broke the expected "user on the right, assistant on the left" transcript structure.

This commit keeps both fixes narrow: the plugin header now delays its split layout and lets summary
cards and controls reflow responsively, while chat message actions live inside each message column so
interaction affordances no longer distort the bubble alignment.

Constraint: Desktop settings and transcript layouts must remain readable in narrower window widths
Rejected: Keep message actions inline with the bubble row | action buttons keep stretching the message lane and blur role alignment
Rejected: Hide rewind and copy entirely until a larger redesign | removes the control instead of fixing the regression
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep transcript actions inside the message column unless a future redesign also redefines bubble alignment rules
Tested: bun run test src/__tests__/pluginsSettings.test.tsx --run; bun run test src/components/chat/MessageList.test.tsx --run; bun run lint; bun run build; agent-browser verification against local mock desktop session
Not-tested: Real Tauri runtime screenshot on a live backend session after these layout changes
2026-04-22 14:55:40 +08:00

47 lines
1.3 KiB
TypeScript

import type { UIAttachment } from '../../types/chat'
import { AttachmentGallery } from './AttachmentGallery'
import { MessageActionBar } from './MessageActionBar'
type Props = {
content: string
attachments?: UIAttachment[]
onRewind?: () => void
rewindLabel?: string
}
export function UserMessage({ content, attachments, onRewind, rewindLabel }: Props) {
const hasText = content.trim().length > 0
return (
<div className="group mb-5 flex justify-end">
<div
data-message-shell="user"
className="flex min-w-0 w-full max-w-[82%] flex-col items-end gap-2 sm:max-w-[78%] lg:max-w-[72%]"
>
{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>
)}
{hasText && (
<MessageActionBar
copyText={content}
copyLabel="Copy prompt"
onRewind={onRewind}
rewindLabel={rewindLabel}
align="end"
/>
)}
</div>
</div>
)
}