import * as React from "react"; import { CheckCheck } from "lucide-react"; import { useAppNavigation } from "@/app/navigation/useAppNavigation"; import { cn } from "@/shared/lib/cn"; import { useProfilePanel } from "@/shared/context/ProfilePanelContext"; import { Markdown } from "@/shared/ui/markdown"; import { UserAvatar } from "@/shared/ui/UserAvatar"; import { useAgentSessionTranscriptVariant } from "../agentSessionTranscriptContext"; import { MessageLinkHoverCue } from "../activityRenderClasses/MessageLinkHoverCue"; import { TranscriptTimestamp } from "../activityRenderClasses/TranscriptTimestamp"; import { useTranscriptBubbleOverflow } from "../activityRenderClasses/useTranscriptBubbleOverflow"; import { compactSummaryTone } from "./CompactToolSummaryRow"; import type { SentMessageLink } from "./messageLinks"; import { SentMessageContextDialog } from "./SentMessageContextDialog"; import { useSentMessageBody } from "./useSentMessageBody"; export function CompactMessageSummary({ args, avatarUrl, description, displayName, duration, hasArgs, hasResult, isError, label, messageLink, preview, pubkey, result, timestamp, }: { args: Record; avatarUrl: string | null; description?: string; displayName: string; duration: string | null; hasArgs: boolean; hasResult: boolean; isError: boolean; label: string; messageLink: SentMessageLink | null; preview: string | null; pubkey: string; result: string; timestamp: string; }) { const [detailsOpen, setDetailsOpen] = React.useState(false); const resolvedContent = useSentMessageBody(messageLink, preview); const variant = useAgentSessionTranscriptVariant(); const { goChannel } = useAppNavigation(); const { openProfilePanel } = useProfilePanel(); const isCompactPreview = variant === "compactPreview"; const isConversation = variant === "conversation"; // Focus mode is a reading view, so sent messages remain fully visible there. // The default activity surface keeps the compact clamp. const shouldClampBubble = !isCompactPreview && !isConversation; const [bubbleRef, hasBubbleOverflow] = useTranscriptBubbleOverflow(shouldClampBubble); const canOpenMessage = !isCompactPreview && messageLink !== null; const mutedTone = compactSummaryTone(); const avatarClassName = cn( "mr-2 mt-1 shrink-0", isCompactPreview ? "size-5" : "size-7", ); const handleBubbleClick = React.useCallback( (event: React.MouseEvent) => { if (!messageLink || isNestedInteractiveTarget(event)) return; event.preventDefault(); event.stopPropagation(); void goChannel(messageLink.channelId, { messageId: messageLink.messageId, }); }, [goChannel, messageLink], ); const handleBubbleKeyDown = React.useCallback( (event: React.KeyboardEvent) => { if ( !messageLink || isNestedInteractiveTarget(event) || (event.key !== "Enter" && event.key !== " ") ) { return; } event.preventDefault(); event.stopPropagation(); void goChannel(messageLink.channelId, { messageId: messageLink.messageId, }); }, [goChannel, messageLink], ); const bubbleLinkProps = canOpenMessage ? { onClick: handleBubbleClick, onKeyDown: handleBubbleKeyDown, role: "link" as const, tabIndex: 0, } : {}; return ( <>
{openProfilePanel && !isCompactPreview ? ( ) : ( )}
{hasBubbleOverflow ? ( ) : null} {canOpenMessage ? : null}
); } function isNestedInteractiveTarget( event: React.MouseEvent | React.KeyboardEvent, ) { const target = event.target instanceof Element ? event.target.closest( "a,button,input,select,textarea,summary,[role='button'],[role='link']", ) : null; return target !== null && target !== event.currentTarget; }