mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Show agent avatars in activity log
Use the selected agent profile avatar in the live activity panel header and assistant transcript rows for clearer session context. Made-with: Cursor
This commit is contained in:
@@ -1,15 +1,18 @@
|
||||
import { Bot, Brain, ChevronDown, Radio, TerminalSquare } from "lucide-react";
|
||||
import { Brain, ChevronDown, Radio, TerminalSquare } from "lucide-react";
|
||||
|
||||
import { cn } from "@/shared/lib/cn";
|
||||
import { Markdown } from "@/shared/ui/markdown";
|
||||
import { UserAvatar } from "@/shared/ui/UserAvatar";
|
||||
import type { TranscriptItem } from "./agentSessionTypes";
|
||||
import { ToolItem } from "./AgentSessionToolItem";
|
||||
|
||||
export function AgentSessionTranscriptList({
|
||||
agentAvatarUrl,
|
||||
agentName,
|
||||
emptyDescription,
|
||||
items,
|
||||
}: {
|
||||
agentAvatarUrl: string | null;
|
||||
agentName: string;
|
||||
emptyDescription: string;
|
||||
items: TranscriptItem[];
|
||||
@@ -33,7 +36,11 @@ export function AgentSessionTranscriptList({
|
||||
>
|
||||
{items.map((item) => (
|
||||
<div key={item.id}>
|
||||
<TranscriptItemView agentName={agentName} item={item} />
|
||||
<TranscriptItemView
|
||||
agentAvatarUrl={agentAvatarUrl}
|
||||
agentName={agentName}
|
||||
item={item}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -41,14 +48,22 @@ export function AgentSessionTranscriptList({
|
||||
}
|
||||
|
||||
function TranscriptItemView({
|
||||
agentAvatarUrl,
|
||||
agentName,
|
||||
item,
|
||||
}: {
|
||||
agentAvatarUrl: string | null;
|
||||
agentName: string;
|
||||
item: TranscriptItem;
|
||||
}) {
|
||||
if (item.type === "message") {
|
||||
return <MessageItem agentName={agentName} item={item} />;
|
||||
return (
|
||||
<MessageItem
|
||||
agentAvatarUrl={agentAvatarUrl}
|
||||
agentName={agentName}
|
||||
item={item}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (item.type === "tool") {
|
||||
return <ToolItem item={item} />;
|
||||
@@ -63,9 +78,11 @@ function TranscriptItemView({
|
||||
}
|
||||
|
||||
function MessageItem({
|
||||
agentAvatarUrl,
|
||||
agentName,
|
||||
item,
|
||||
}: {
|
||||
agentAvatarUrl: string | null;
|
||||
agentName: string;
|
||||
item: Extract<TranscriptItem, { type: "message" }>;
|
||||
}) {
|
||||
@@ -80,9 +97,12 @@ function MessageItem({
|
||||
<div className="group relative flex min-w-0 flex-1 flex-col items-start gap-1">
|
||||
<div className="flex items-center gap-1.5 text-xs">
|
||||
{isAssistant ? (
|
||||
<span className="flex h-5 w-5 items-center justify-center">
|
||||
<Bot className="h-3.5 w-3.5 text-muted-foreground" />
|
||||
</span>
|
||||
<UserAvatar
|
||||
avatarUrl={agentAvatarUrl}
|
||||
className="rounded-full shadow-none"
|
||||
displayName={agentName}
|
||||
size="xs"
|
||||
/>
|
||||
) : null}
|
||||
<span className="font-medium text-foreground">{label}</span>
|
||||
</div>
|
||||
|
||||
@@ -25,6 +25,7 @@ import { useObserverEvents } from "./useObserverEvents";
|
||||
|
||||
type ManagedAgentSessionPanelProps = {
|
||||
agent: ManagedAgent;
|
||||
agentAvatarUrl?: string | null;
|
||||
channelId?: string | null;
|
||||
className?: string;
|
||||
emptyDescription?: string;
|
||||
@@ -34,6 +35,7 @@ type ManagedAgentSessionPanelProps = {
|
||||
|
||||
export function ManagedAgentSessionPanel({
|
||||
agent,
|
||||
agentAvatarUrl = null,
|
||||
channelId = null,
|
||||
className,
|
||||
emptyDescription = "Mention this agent in a channel to watch the next turn.",
|
||||
@@ -78,6 +80,7 @@ export function ManagedAgentSessionPanel({
|
||||
) : null}
|
||||
|
||||
<SessionBody
|
||||
agentAvatarUrl={agentAvatarUrl}
|
||||
agentName={agent.name}
|
||||
connectionState={connectionState}
|
||||
emptyDescription={emptyDescription}
|
||||
@@ -127,6 +130,7 @@ function SessionHeader({
|
||||
}
|
||||
|
||||
function SessionBody({
|
||||
agentAvatarUrl,
|
||||
agentName,
|
||||
connectionState,
|
||||
emptyDescription,
|
||||
@@ -136,6 +140,7 @@ function SessionBody({
|
||||
showRaw,
|
||||
transcript,
|
||||
}: {
|
||||
agentAvatarUrl: string | null;
|
||||
agentName: string;
|
||||
connectionState: ConnectionState;
|
||||
emptyDescription: string;
|
||||
@@ -160,6 +165,7 @@ function SessionBody({
|
||||
)}
|
||||
>
|
||||
<AgentSessionTranscriptList
|
||||
agentAvatarUrl={agentAvatarUrl}
|
||||
agentName={agentName}
|
||||
emptyDescription={emptyDescription}
|
||||
items={transcript}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import type * as React from "react";
|
||||
import { Activity, Bot, CircleDot, Octagon, X } from "lucide-react";
|
||||
import { Activity, CircleDot, Octagon, X } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { ManagedAgentSessionPanel } from "@/features/agents/ui/ManagedAgentSessionPanel";
|
||||
import type { UserProfileLookup } from "@/features/profile/lib/identity";
|
||||
import { cancelManagedAgentTurn } from "@/shared/api/agentControl";
|
||||
import type { Channel, ManagedAgent } from "@/shared/api/types";
|
||||
import { useStickToBottom } from "@/shared/hooks/useStickToBottom";
|
||||
import { Badge } from "@/shared/ui/badge";
|
||||
import { Button } from "@/shared/ui/button";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip";
|
||||
import { UserAvatar } from "@/shared/ui/UserAvatar";
|
||||
|
||||
type AgentSessionThreadPanelProps = {
|
||||
agent: ManagedAgent;
|
||||
@@ -18,6 +20,7 @@ type AgentSessionThreadPanelProps = {
|
||||
onClose: () => void;
|
||||
onResetWidth: () => void;
|
||||
onResizeStart: (event: React.PointerEvent<HTMLButtonElement>) => void;
|
||||
profiles?: UserProfileLookup;
|
||||
widthPx: number;
|
||||
};
|
||||
|
||||
@@ -29,10 +32,13 @@ export function AgentSessionThreadPanel({
|
||||
onClose,
|
||||
onResetWidth,
|
||||
onResizeStart,
|
||||
profiles,
|
||||
widthPx,
|
||||
}: AgentSessionThreadPanelProps) {
|
||||
const isLive = agent.status === "running";
|
||||
const { ref: scrollRef, onScroll } = useStickToBottom<HTMLDivElement>();
|
||||
const agentAvatarUrl =
|
||||
profiles?.[agent.pubkey.toLowerCase()]?.avatarUrl ?? null;
|
||||
|
||||
async function handleInterruptTurn() {
|
||||
try {
|
||||
@@ -74,7 +80,12 @@ export function AgentSessionThreadPanel({
|
||||
</button>
|
||||
|
||||
<div className="flex items-center gap-3 border-b border-border/70 px-4 py-2.5">
|
||||
<Bot className="h-4 w-4 shrink-0 text-muted-foreground" />
|
||||
<UserAvatar
|
||||
avatarUrl={agentAvatarUrl}
|
||||
className="shrink-0 rounded-full shadow-none"
|
||||
displayName={agent.name}
|
||||
size="sm"
|
||||
/>
|
||||
<div className="min-w-0 flex-1">
|
||||
<h2 className="truncate text-sm font-semibold tracking-tight">
|
||||
{agent.name}
|
||||
@@ -141,6 +152,7 @@ export function AgentSessionThreadPanel({
|
||||
channelId={channel.id}
|
||||
className="border-0 bg-transparent p-0 shadow-none"
|
||||
emptyDescription={`Mention ${agent.name} in the channel to see its work here.`}
|
||||
agentAvatarUrl={agentAvatarUrl}
|
||||
showHeader={false}
|
||||
showRaw={false}
|
||||
/>
|
||||
|
||||
@@ -355,6 +355,7 @@ export const ChannelPane = React.memo(function ChannelPane({
|
||||
onClose={onCloseAgentSession}
|
||||
onResetWidth={handleThreadPanelWidthReset}
|
||||
onResizeStart={handleThreadPanelResizeStart}
|
||||
profiles={profiles}
|
||||
widthPx={threadPanelWidthPx}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
Reference in New Issue
Block a user