"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}>
);
};