"use client"; import { makeAssistantToolUI } from "@assistant-ui/react"; import { AlertCircleIcon, ExternalLinkIcon, LinkIcon } from "lucide-react"; import { z } from "zod"; import { MediaCard, MediaCardErrorBoundary, MediaCardLoading, parseSerializableMediaCard, type SerializableMediaCard, } from "@/components/tool-ui/media-card"; // ============================================================================ // Zod Schemas // ============================================================================ /** * Schema for link_preview tool arguments */ const LinkPreviewArgsSchema = z.object({ url: z.string(), title: z.string().nullish(), }); /** * Schema for link_preview tool result */ const LinkPreviewResultSchema = z.object({ id: z.string(), assetId: z.string(), kind: z.literal("link"), href: z.string(), title: z.string(), description: z.string().nullish(), thumb: z.string().nullish(), domain: z.string().nullish(), error: z.string().nullish(), }); // ============================================================================ // Types // ============================================================================ type LinkPreviewArgs = z.infer; type LinkPreviewResult = z.infer; /** * Error state component shown when link preview fails */ function LinkPreviewErrorState({ url, error }: { url: string; error: string }) { return (

Failed to load preview

{url}

{error}

); } /** * Cancelled state component */ function LinkPreviewCancelledState({ url }: { url: string }) { return (

Preview: {url}

); } /** * Parsed MediaCard component with error handling */ function ParsedMediaCard({ result }: { result: unknown }) { const card = parseSerializableMediaCard(result); return ( { if (id === "open" && card.href) { window.open(card.href, "_blank", "noopener,noreferrer"); } }} /> ); } /** * Link Preview Tool UI Component * * This component is registered with assistant-ui to render a rich * link preview card when the link_preview tool is called by the agent. * * It displays website metadata including: * - Title and description * - Thumbnail/Open Graph image * - Domain name * - Clickable link to open in new tab */ export const LinkPreviewToolUI = makeAssistantToolUI({ toolName: "link_preview", render: function LinkPreviewUI({ args, result, status }) { const url = args.url || "Unknown URL"; // Loading state - tool is still running if (status.type === "running" || status.type === "requires-action") { return (
); } // Incomplete/cancelled state if (status.type === "incomplete") { if (status.reason === "cancelled") { return ; } if (status.reason === "error") { return ( ); } } // No result yet if (!result) { return (
); } // Error result from the tool if (result.error) { return ; } // Success - render the media card return (
); }, }); // ============================================================================ // Multi Link Preview Schemas // ============================================================================ /** * Schema for multi_link_preview tool arguments */ const MultiLinkPreviewArgsSchema = z.object({ urls: z.array(z.string()), }); /** * Schema for error items in multi_link_preview result */ const MultiLinkPreviewErrorSchema = z.object({ url: z.string(), error: z.string(), }); /** * Schema for multi_link_preview tool result */ const MultiLinkPreviewResultSchema = z.object({ previews: z.array(LinkPreviewResultSchema), errors: z.array(MultiLinkPreviewErrorSchema).nullish(), }); type MultiLinkPreviewArgs = z.infer; type MultiLinkPreviewResult = z.infer; export const MultiLinkPreviewToolUI = makeAssistantToolUI< MultiLinkPreviewArgs, MultiLinkPreviewResult >({ toolName: "multi_link_preview", render: function MultiLinkPreviewUI({ args, result, status }) { const urls = args.urls || []; // Loading state if (status.type === "running" || status.type === "requires-action") { return (
{urls.slice(0, 4).map((url, index) => ( ))}
); } // Incomplete state if (status.type === "incomplete") { return (

Link previews cancelled

); } // No result if (!result || !result.previews) { return null; } // Render grid of previews return (
{result.previews.map((preview) => ( ))} {result.errors?.map((err) => ( ))}
); }, }); export { LinkPreviewArgsSchema, LinkPreviewResultSchema, MultiLinkPreviewArgsSchema, MultiLinkPreviewResultSchema, type LinkPreviewArgs, type LinkPreviewResult, type MultiLinkPreviewArgs, type MultiLinkPreviewResult, };