61 lines
1.8 KiB
TypeScript
61 lines
1.8 KiB
TypeScript
"use client";
|
|
|
|
import type { ReactNode } from "react";
|
|
|
|
import { desensitize } from "@/lib/desensitize";
|
|
import { cn } from "@/lib/utils";
|
|
import { getAvatar } from "@/roundtable-planning/components/Avatars";
|
|
import { MessageBubble } from "@/roundtable-planning/components/MessageBubble";
|
|
import type { ReportSpeaker } from "@/report-collaboration/state/speaker";
|
|
|
|
interface ReportSpeakerShellProps {
|
|
speaker: ReportSpeaker;
|
|
onAvatarClick?: (agentRunId: string) => void;
|
|
avatarSelected?: boolean;
|
|
children: ReactNode;
|
|
}
|
|
|
|
/**
|
|
* 会商第二步气泡壳:头像 + 席位名 + 正文(步骤条 / 思考 / 输出仍走 MessageList)。
|
|
*/
|
|
export function ReportSpeakerShell({
|
|
speaker,
|
|
onAvatarClick,
|
|
avatarSelected = false,
|
|
children,
|
|
}: ReportSpeakerShellProps) {
|
|
const avatarInner = getAvatar(speaker.avatarType, "h-9 w-9 shrink-0");
|
|
const canFilter = Boolean(speaker.agentRunId && onAvatarClick);
|
|
const avatar = canFilter ? (
|
|
<button
|
|
type="button"
|
|
onClick={() => onAvatarClick?.(speaker.agentRunId!)}
|
|
aria-pressed={avatarSelected}
|
|
title={
|
|
avatarSelected
|
|
? "取消筛选,显示全部发言"
|
|
: `只看 ${desensitize(speaker.name)} 的发言`
|
|
}
|
|
className={cn(
|
|
"shrink-0 cursor-pointer rounded-full transition hover:opacity-80 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary/60",
|
|
avatarSelected && "ring-2 ring-primary ring-offset-1 ring-offset-white",
|
|
)}
|
|
>
|
|
{avatarInner}
|
|
</button>
|
|
) : (
|
|
avatarInner
|
|
);
|
|
|
|
return (
|
|
<MessageBubble
|
|
avatar={avatar}
|
|
senderName={speaker.name}
|
|
badge={speaker.role && speaker.role !== speaker.name ? speaker.role : undefined}
|
|
time={speaker.time}
|
|
>
|
|
{children}
|
|
</MessageBubble>
|
|
);
|
|
}
|