import React from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { X } from 'lucide-react'; import { cn } from '@/lib/utils'; import { UsagePreview } from './usage-preview'; import { FloatingToolPreview, ToolCallInput } from './floating-tool-preview'; import { isLocalMode } from '@/lib/config'; export interface ChatSnackProps { // Tool preview props toolCalls?: ToolCallInput[]; toolCallIndex?: number; onExpandToolPreview?: () => void; agentName?: string; showToolPreview?: boolean; // Usage preview props showUsagePreview?: 'tokens' | 'upgrade' | false; subscriptionData?: any; onCloseUsage?: () => void; onOpenUpgrade?: () => void; // General props isVisible?: boolean; } const SNACK_LAYOUT_ID = 'chat-snack-float'; const SNACK_CONTENT_LAYOUT_ID = 'chat-snack-content'; export const ChatSnack: React.FC = ({ toolCalls = [], toolCallIndex = 0, onExpandToolPreview, agentName, showToolPreview = false, showUsagePreview = false, subscriptionData, onCloseUsage, onOpenUpgrade, isVisible = false, }) => { const [currentView, setCurrentView] = React.useState(0); // Determine what notifications we have - match exact rendering conditions const notifications = []; // Tool notification: only if we have tool calls and showToolPreview is true if (showToolPreview && toolCalls.length > 0) { notifications.push('tool'); } // Usage notification: must match ALL rendering conditions if (showUsagePreview && !isLocalMode() && subscriptionData) { notifications.push('usage'); } const totalNotifications = notifications.length; const hasMultiple = totalNotifications > 1; // Reset currentView when notifications change React.useEffect(() => { if (currentView >= totalNotifications && totalNotifications > 0) { setCurrentView(0); } }, [totalNotifications, currentView]); // Auto-cycle through notifications React.useEffect(() => { if (!hasMultiple || !isVisible) return; const interval = setInterval(() => { setCurrentView((prev) => (prev + 1) % totalNotifications); }, 20000); return () => clearInterval(interval); }, [hasMultiple, isVisible, totalNotifications, currentView]); // Reset timer when currentView changes if (!isVisible || totalNotifications === 0) return null; const currentNotification = notifications[currentView]; const renderContent = () => { if (currentNotification === 'tool' && showToolPreview) { return ( { })} agentName={agentName} isVisible={true} showIndicators={hasMultiple} indicatorIndex={currentView} indicatorTotal={totalNotifications} onIndicatorClick={(index) => setCurrentView(index)} /> ); } if (currentNotification === 'usage' && showUsagePreview && !isLocalMode()) { return ( { // Don't trigger if clicking on indicators or close button const target = e.target as HTMLElement; const isIndicatorClick = target.closest('[data-indicator-click]'); const isCloseClick = target.closest('[data-close-click]'); if (!isIndicatorClick && !isCloseClick && onOpenUpgrade) { onOpenUpgrade(); } }} > { if (onCloseUsage) onCloseUsage(); // If there are other notifications, switch to them if (totalNotifications > 1) { const remainingNotifications = notifications.filter(n => n !== 'usage'); if (remainingNotifications.length > 0) { setCurrentView(0); // Switch to first remaining notification } } }} hasMultiple={hasMultiple} showIndicators={hasMultiple} currentIndex={currentView} totalCount={totalNotifications} onIndicatorClick={(index) => setCurrentView(index)} onOpenUpgrade={onOpenUpgrade} /> ); } return null; }; return ( {isVisible && ( {renderContent()} )} ); };