"use client"; import { makeAssistantToolUI } from "@assistant-ui/react"; import { z } from "zod"; import { cn } from "@/lib/utils"; import { Star, TrendingUp, TrendingDown, Bell, Trash2, Plus } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { ChainIcon } from "@/components/crypto/ChainIcon"; // Schema for watchlist token const WatchlistTokenSchema = z.object({ id: z.string(), symbol: z.string(), name: z.string(), chain: z.string(), price: z.number(), priceChange24h: z.number(), alertCount: z.number().optional(), }); // Schema for watchlist display tool arguments export const WatchlistDisplayArgsSchema = z.object({ tokens: z.array(WatchlistTokenSchema), }); export type WatchlistDisplayArgs = z.infer; // Schema for watchlist display result export const WatchlistDisplayResultSchema = z.object({ success: z.boolean(), message: z.string().optional(), }); export type WatchlistDisplayResult = z.infer; const formatPrice = (price: number): string => { if (price < 0.00001) return `$${price.toExponential(2)}`; if (price < 1) return `$${price.toFixed(6)}`; return `$${price.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; }; /** * WatchlistDisplayToolUI - Displays user's watchlist inline in chat * Used when AI responds to "show my watchlist" or similar commands */ export const WatchlistDisplayToolUI = makeAssistantToolUI({ toolName: "show_watchlist", render: ({ args, status }) => { const isLoading = status.type === "running"; const tokens = args.tokens || []; if (tokens.length === 0) { return (

Your watchlist is empty

Say "Add [token] to my watchlist" to start tracking

); } // Find best and worst performers const sortedByChange = [...tokens].sort((a, b) => b.priceChange24h - a.priceChange24h); const bestPerformer = sortedByChange[0]; const worstPerformer = sortedByChange[sortedByChange.length - 1]; return (
Your Watchlist {tokens.length} {isLoading && Loading...}
{/* Token List */}
{tokens.map((token) => (
{token.symbol} {token.alertCount && token.alertCount > 0 && ( {token.alertCount} )}
{token.name}

{formatPrice(token.price)}

= 0 ? "text-green-500" : "text-red-500" )}> {token.priceChange24h >= 0 ? : } {token.priceChange24h >= 0 ? "+" : ""}{token.priceChange24h.toFixed(1)}%

))}
{/* Summary */} {tokens.length > 1 && (
{bestPerformer.symbol} is your best performer (+{bestPerformer.priceChange24h.toFixed(1)}%) {worstPerformer.priceChange24h < 0 && ( {worstPerformer.symbol} needs attention ({worstPerformer.priceChange24h.toFixed(1)}%) )}
)}
); }, });