import { ActionBarPrimitive, AssistantIf, ErrorPrimitive, MessagePrimitive, useAssistantState, } from "@assistant-ui/react"; import { useAtomValue } from "jotai"; import { CheckIcon, CopyIcon, DownloadIcon, RefreshCwIcon } from "lucide-react"; import type { FC } from "react"; import { useContext, useState } from "react"; import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { BranchPicker } from "@/components/assistant-ui/branch-picker"; import { MarkdownText } from "@/components/assistant-ui/markdown-text"; import { ThinkingStepsContext, ThinkingStepsDisplay, } from "@/components/assistant-ui/thinking-steps"; import { ToolFallback } from "@/components/assistant-ui/tool-fallback"; import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button"; import { CommentPanelContainer } from "@/components/chat-comments/comment-panel-container/comment-panel-container"; import { CommentTrigger } from "@/components/chat-comments/comment-trigger/comment-trigger"; import { useComments } from "@/hooks/use-comments"; export const MessageError: FC = () => { return ( ); }; /** * Custom component to render thinking steps from Context */ const ThinkingStepsPart: FC = () => { const thinkingStepsMap = useContext(ThinkingStepsContext); // Get the current message ID to look up thinking steps const messageId = useAssistantState(({ message }) => message?.id); const thinkingSteps = thinkingStepsMap.get(messageId) || []; // Check if this specific message is currently streaming // A message is streaming if: thread is running AND this is the last assistant message const isThreadRunning = useAssistantState(({ thread }) => thread.isRunning); const isLastMessage = useAssistantState(({ message }) => message?.isLast ?? false); const isMessageStreaming = isThreadRunning && isLastMessage; if (thinkingSteps.length === 0) return null; return (
); }; const AssistantMessageInner: FC = () => { return ( <> {/* Render thinking steps from message content - this ensures proper scroll tracking */}
); }; function parseMessageId(assistantUiMessageId: string | undefined): number | null { if (!assistantUiMessageId) return null; const match = assistantUiMessageId.match(/^msg-(\d+)$/); return match ? Number.parseInt(match[1], 10) : null; } export const AssistantMessage: FC = () => { const [isCommentPanelOpen, setIsCommentPanelOpen] = useState(false); const messageId = useAssistantState(({ message }) => message?.id); const searchSpaceId = useAtomValue(activeSearchSpaceIdAtom); const dbMessageId = parseMessageId(messageId); const { data: commentsData } = useComments({ messageId: dbMessageId ?? 0, enabled: !!dbMessageId, }); const commentCount = commentsData?.total_count ?? 0; return ( {/* Comment trigger and floating panel */} {dbMessageId && searchSpaceId && (
setIsCommentPanelOpen(!isCommentPanelOpen)} /> {isCommentPanelOpen && (
setIsCommentPanelOpen(false)} maxHeight={400} />
)}
)}
); }; const AssistantActionBar: FC = () => { return ( message.isCopied}> !message.isCopied}> ); };