"use client"; import { MarkdownText } from "@/components/assistant-ui/markdown-text"; import { Reasoning, ReasoningContent, ReasoningRoot, ReasoningText, ReasoningTrigger, } from "@/components/assistant-ui/reasoning"; import { ToolFallback } from "@/components/assistant-ui/tool-fallback"; import { ToolGroupContent, ToolGroupRoot, ToolGroupTrigger, } from "@/components/assistant-ui/tool-group"; import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button"; import { UnknownComponentFallback, componentsAllowlist, } from "@/components/gui"; import { RENDER_GUI_TOOL_NAME, isLeakedRenderGuiText, parseRenderGuiResult, } from "@/lib/render-gui-tool"; import { cn } from "@/lib/utils"; import type { GenerativeUISpec } from "@assistant-ui/react"; import { ActionBarPrimitive, AuiIf, BranchPickerPrimitive, ErrorPrimitive, getMcpAppFromToolPart, MessagePrimitive, useAuiState, } from "@assistant-ui/react"; import { CheckIcon, ChevronLeftIcon, ChevronRightIcon, CopyIcon, RefreshCwIcon, } from "lucide-react"; import { type FC, useEffect, useRef } from "react"; type AssistantMessageGuiProps = { /** Set false in dev to surface the dropped-bridge warning. */ bridgeEnabled?: boolean; }; const AllowlistedGenerativeUI = ({ spec }: { spec?: GenerativeUISpec }) => ( ); const useDroppedGenerativeUIWarning = (bridgeEnabled: boolean) => { const content = useAuiState((s) => s.message.content); const warned = useRef(false); useEffect(() => { if ( process.env.NODE_ENV === "production" || bridgeEnabled || warned.current ) { return; } const hasRenderGui = content.some( (part) => part.type === "tool-call" && part.toolName === RENDER_GUI_TOOL_NAME && part.result != null && parseRenderGuiResult(part.result) != null, ); const hasUnhandledGenerativeUI = content.some( (part) => part.type === "generative-ui", ); if (hasRenderGui || hasUnhandledGenerativeUI) { warned.current = true; console.warn( "[generative-ui] Message contains render_gui or generative-ui parts but the bridge is disabled. " + "Enable the bridge in assistant-message-gui.tsx or wire MessagePrimitive.GenerativeUI.", ); } }, [bridgeEnabled, content]); }; export const AssistantMessageGui: FC = ({ bridgeEnabled = true, }) => { useDroppedGenerativeUIWarning(bridgeEnabled); const hasBridgedRenderGui = useAuiState((s) => s.message.content.some( (part) => part.type === "tool-call" && part.toolName === RENDER_GUI_TOOL_NAME && parseRenderGuiResult(part.result) != null, ), ); const ACTION_BAR_PT = "pt-1.5"; const ACTION_BAR_HEIGHT = `-mb-7.5 min-h-7.5 ${ACTION_BAR_PT}`; return (
{ if (part.type === "reasoning") { return ["group-chainOfThought", "group-reasoning"]; } if (part.type === "tool-call") { if (getMcpAppFromToolPart(part)) return []; if (part.toolName === RENDER_GUI_TOOL_NAME) return []; return ["group-chainOfThought", "group-tool"]; } return []; }} > {({ part, children }) => { switch (part.type) { case "group-chainOfThought": return
{children}
; case "group-reasoning": { const running = part.status.type === "running"; return ( {children} ); } case "group-tool": return ( {children} ); case "text": if ( bridgeEnabled && hasBridgedRenderGui && isLeakedRenderGuiText(part.text) ) { return null; } return ; case "reasoning": return ; case "generative-ui": return bridgeEnabled ? : null; case "tool-call": { if (bridgeEnabled && part.toolName === RENDER_GUI_TOOL_NAME) { const spec = parseRenderGuiResult(part.result); if (spec) return ; } return part.toolUI ?? ; } default: return null; } }}
/ s.message.isCopied}> !s.message.isCopied}>
); };