import { cloneElement, useCallback, useEffect, useState, type FocusEvent as ReactFocusEvent, type JSX, type PointerEvent as ReactPointerEvent, type ReactElement, type ReactNode } from "react"; import { ExternalLink, Info } from "lucide-react"; import { Button, cn, Tooltip, TooltipContent, TooltipTrigger } from "@tutti-os/ui-system"; import { managedAgentRoundedIconUrl } from "../shared/managedAgentIcons"; import { workspaceAgentProviderLabel } from "../shared/workspaceAgentProviderLabel"; import { userAvatarPlaceholderUrl } from "../shared/userAvatarPlaceholder"; import { isWaitingMessageCenterItem, type WorkspaceAgentMessageCenterIdentity, type WorkspaceAgentMessageCenterItem } from "./workspaceAgentMessageCenterModel"; export function MessageCenterOpenChatButton({ actionsAccessory, alwaysVisible = false, item, label, onOpenChat, provider }: { actionsAccessory?: ReactNode; alwaysVisible?: boolean; item: WorkspaceAgentMessageCenterItem; label: string; onOpenChat: (input: { agentSessionId: string; provider: string }) => void; provider: string; }): JSX.Element { "use memo"; return (
{actionsAccessory}
); } export function MessageCenterIdentityLabel({ agentAvatarUrl, agentName, identity, provider }: { agentAvatarUrl?: string | null; agentName?: string | null; identity: WorkspaceAgentMessageCenterIdentity | null; provider: string; }): JSX.Element { "use memo"; if (!identity) { return ( ); } const resolvedAgentAvatarUrl = identity.agentAvatarUrl?.trim() || agentAvatarUrl?.trim() || managedAgentRoundedIconUrl(provider); const title = `${identity.userName} & ${identity.agentName}`; return ( {identity.userName} & {identity.agentName} ); } export function MessageCenterIdentityAvatarMark({ agentAvatarUrl, identity, provider, userId }: { agentAvatarUrl?: string | null; identity: WorkspaceAgentMessageCenterIdentity | null; provider: string; userId?: string | null; }): JSX.Element { "use memo"; const userName = identity?.userName.trim() || userId?.trim() || ""; if (!userName) { return ( ); } return ( ); } export function MessageCenterIdentityAvatarStack({ agentAvatarUrl, userAvatarUrl: rawUserAvatarUrl, userName }: { agentAvatarUrl: string; userAvatarUrl?: string; userName: string; }): JSX.Element { "use memo"; const [userAvatarFailed, setUserAvatarFailed] = useState(false); const userAvatarUrl = rawUserAvatarUrl?.trim() ?? ""; const userImageUrl = userAvatarUrl.length > 0 && !userAvatarFailed ? userAvatarUrl : userAvatarPlaceholderUrl; useEffect(() => { setUserAvatarFailed(false); }, [userAvatarUrl]); return ( ); } export function ProjectPathInfo({ path }: { path: string }): JSX.Element { "use memo"; return ( ); } type LazyTooltipChildProps = { onBlur?: (event: ReactFocusEvent) => void; onFocus?: (event: ReactFocusEvent) => void; onPointerEnter?: (event: ReactPointerEvent) => void; onPointerLeave?: (event: ReactPointerEvent) => void; }; export function LazyMessageCenterTooltip({ align, children, className, content, side }: { align?: "center" | "end" | "start"; children: ReactElement; className?: string; content: ReactNode; side?: "bottom" | "left" | "right" | "top"; }): JSX.Element { "use memo"; const [hydrated, setHydrated] = useState(false); const [open, setOpen] = useState(false); const showTooltip = useCallback(() => { setHydrated(true); setOpen(true); }, []); const hideTooltip = useCallback(() => { setOpen(false); }, []); const trigger = cloneElement(children, { onBlur: (event: ReactFocusEvent) => { children.props.onBlur?.(event); hideTooltip(); }, onFocus: (event: ReactFocusEvent) => { children.props.onFocus?.(event); showTooltip(); }, onPointerEnter: (event: ReactPointerEvent) => { children.props.onPointerEnter?.(event); showTooltip(); }, onPointerLeave: (event: ReactPointerEvent) => { children.props.onPointerLeave?.(event); hideTooltip(); } }); if (!hydrated) { return trigger; } return ( {trigger} {content} ); } function AgentProviderLabel({ agentAvatarUrl, agentName, provider }: { agentAvatarUrl?: string | null; agentName?: string | null; provider: string; }): JSX.Element { "use memo"; const label = agentName?.trim() || workspaceAgentProviderLabel(provider); return ( {label} ); } export function statusClass(item: WorkspaceAgentMessageCenterItem): string { if (isWaitingMessageCenterItem(item)) { return "waiting"; } if (item.status === "idle") { return "completed"; } return item.status; } export type MessageCenterStatusTone = | "amber" | "blue" | "green" | "neutral" | "red"; export function messageCenterStatusTone( item: WorkspaceAgentMessageCenterItem ): MessageCenterStatusTone { if (isWaitingMessageCenterItem(item)) { return "amber"; } if (item.status === "completed" || item.status === "idle") { return "green"; } if (item.status === "canceled") { return "amber"; } if (item.status === "failed") { return "red"; } if (item.status === "working") { return "blue"; } return "neutral"; } export function messageCenterStatusToneClass( tone: MessageCenterStatusTone ): string { switch (tone) { case "amber": return "text-[var(--state-warning)]"; case "blue": return "text-[var(--status-running)]"; case "green": return "text-[var(--state-success)]"; case "red": return "text-[var(--state-danger)]"; case "neutral": return "text-[var(--text-secondary)]"; } }