fix: 按 speaker.type 区分消息左右与头像,并改为 iMessage 气泡样式

main
sp mac bookpro 2605 2026-06-08 01:15:12 +08:00
parent b1df40e983
commit e6116c1c80
5 changed files with 26 additions and 19 deletions

View File

@ -15,7 +15,11 @@ export interface ToolCallTrace {
export interface ChatMessage {
id: string;
role: 'user' | 'assistant';
role: 'user' | 'assistant' | 'agent' | 'system';
speaker?: {
id: string;
type: 'user' | 'agent';
};
content: string;
reasoning?: string | null;
parentId?: string | null;
@ -73,4 +77,3 @@ export const ChatAttachmentsAPI = {
return api.post<{ files: ChatAttachment[] }>('/chat/attachments', fd).then((r) => r.data);
}
};

View File

@ -60,7 +60,7 @@ export default function ChatBody(props: {
agentList={agentList}
currentAgentId={currentAgentId}
highlighted={highlightId === m.id}
branch={m.role === 'assistant' && m.parentId ? branches[m.parentId] : undefined}
branch={((m as any)?.speaker?.type ? (m as any).speaker.type === 'agent' : m.role === 'assistant') && m.parentId ? branches[m.parentId] : undefined}
busy={sending}
onRegenerate={onRegenerate}
onSwitchBranch={onSwitchBranch}
@ -76,7 +76,9 @@ export default function ChatBody(props: {
const streamingAgent = agentList.find(a => a.id === streamingAgentId);
if (streamingAgent) {
return (
<Avatar src={streamingAgent.avatar} size={36} style={{ flexShrink: 0, marginTop: 2 }} />
<Avatar src={streamingAgent.avatar} size={36} style={{ flexShrink: 0, marginTop: 2, backgroundColor: '#52c41a' }}>
{streamingAgent.name?.charAt(0)?.toUpperCase() || 'A'}
</Avatar>
);
}
return null;

View File

@ -14,7 +14,7 @@ function summarize(content: string) {
export default function ChatOutline(props: { messages: ChatMessage[]; onJump: (id: string) => void; activeId?: string | null }) {
const { messages, onJump, activeId } = props;
const items = messages.filter((m) => m.role === 'assistant');
const items = messages.filter((m) => ((m as any)?.speaker?.type ? (m as any).speaker.type === 'agent' : m.role === 'assistant'));
if (items.length === 0) return null;
@ -38,4 +38,3 @@ export default function ChatOutline(props: { messages: ChatMessage[]; onJump: (i
</aside>
);
}

View File

@ -19,8 +19,12 @@ export default function MessageItem(props: {
}) {
const { message, agentList, currentAgentId, highlighted, branch, busy, onRegenerate, onSwitchBranch, onCopy } = props;
const speakerType = (message as any)?.speaker?.type as ('user' | 'agent' | undefined);
const isUser = speakerType ? speakerType === 'user' : message.role === 'user';
const bubbleRole = isUser ? 'user' : 'assistant';
// 获取回答者 Agent 信息
const answerAgentId = message.agent_id || (message.role === 'assistant' ? currentAgentId : undefined);
const answerAgentId = message.agent_id || (!isUser ? currentAgentId : undefined);
const answerAgent = answerAgentId ? agentList.find(a => a.id === answerAgentId) : undefined;
const hasBranches = !!branch && branch.total > 1;
const activeIdx = branch?.activeIndex ?? 0;
@ -49,9 +53,8 @@ export default function MessageItem(props: {
transition: 'background 0.4s, padding 0.4s'
}}
>
{message.role === 'assistant' ? (
{!isUser ? (
<div style={{ display: 'flex', gap: 12, alignItems: 'flex-start' }}>
{/* AGENT: 头像在左侧,内容在右侧(靠左对齐) */}
<Avatar
src={answerAgent?.avatar}
size={36}
@ -74,7 +77,7 @@ export default function MessageItem(props: {
{answerAgent?.name || 'AI'}
</span>
</div>
<div className={`bubble ${message.role}`}>
<div className={`bubble ${bubbleRole}`}>
<Markdown>{message.content}</Markdown>
</div>
<div className="monica-msg-actions">
@ -124,8 +127,7 @@ export default function MessageItem(props: {
</div>
) : (
<div style={{ display: 'flex', gap: 12, alignItems: 'flex-start', justifyContent: 'flex-end' }}>
{/* 用户: 头像在右侧,内容在左侧(靠右对齐) */}
<div style={{ maxWidth: '78%', display: 'flex', flexDirection: 'column', alignItems: 'flex-end' }}>
<div style={{ flex: 1, minWidth: 0, maxWidth: '78%', display: 'flex', flexDirection: 'column', alignItems: 'flex-end' }}>
<div style={{
display: 'flex',
alignItems: 'center',
@ -140,7 +142,7 @@ export default function MessageItem(props: {
</span>
</div>
<div className={`bubble ${message.role}`}>
<div className={`bubble ${bubbleRole}`}>
{message.content.includes('![image](') ? (
<Markdown>{message.content}</Markdown>
) : (

View File

@ -628,6 +628,7 @@ body {
.bubble {
max-width: 78%;
display: inline-block;
padding: 14px 18px;
border-radius: 14px;
margin-bottom: 14px;
@ -638,18 +639,18 @@ body {
}
.bubble.user {
background: var(--color-brand-soft);
color: var(--color-text);
background: #0a84ff;
color: #ffffff;
margin-left: auto;
border-bottom-right-radius: 5px;
}
.bubble.assistant {
background: #ffffff;
color: var(--color-text);
border: 1px solid var(--color-border);
background: #e9e9eb;
color: #111827;
border: 0;
border-bottom-left-radius: 5px;
box-shadow: var(--shadow-xs);
box-shadow: none;
}
.bubble.assistant p {