import React, { useState, useEffect } from 'react'; import { Search, CheckCircle, AlertTriangle, ExternalLink, Image as ImageIcon, Loader2, Globe, FileText, Clock, BookOpen, CalendarDays, ChevronDown, ChevronUp, Check } from 'lucide-react'; import { ToolViewProps } from './types'; import { extractSearchQuery, extractSearchResults, cleanUrl, formatTimestamp, getToolTitle, } from './utils'; import { cn } from '@/lib/utils'; import { useTheme } from 'next-themes'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Progress } from '@/components/ui/progress'; import { ScrollArea } from "@/components/ui/scroll-area"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; function truncateText(text: string, maxLength: number = 70) { return text.length > maxLength ? text.substring(0, maxLength) + '...' : text; } export function WebSearchToolView({ name = 'web-search', assistantContent, toolContent, assistantTimestamp, toolTimestamp, isSuccess = true, isStreaming = false, }: ToolViewProps) { const { resolvedTheme } = useTheme(); const isDarkTheme = resolvedTheme === 'dark'; const [progress, setProgress] = useState(0); const [expandedResults, setExpandedResults] = useState>({}); const query = extractSearchQuery(assistantContent); const searchResults = extractSearchResults(toolContent); const toolTitle = getToolTitle(name); // Extract additional data from response const [answer, setAnswer] = useState(null); const [images, setImages] = useState([]); // Toggle result expansion const toggleExpand = (idx: number) => { setExpandedResults(prev => ({ ...prev, [idx]: !prev[idx] })); }; // Simulate progress when streaming useEffect(() => { if (isStreaming) { const timer = setInterval(() => { setProgress((prevProgress) => { if (prevProgress >= 95) { clearInterval(timer); return prevProgress; } return prevProgress + 5; }); }, 300); return () => clearInterval(timer); } else { setProgress(100); } }, [isStreaming]); useEffect(() => { if (toolContent) { try { const parsedContent = JSON.parse(toolContent); // Check if it's the response format with answer if (parsedContent.answer && typeof parsedContent.answer === 'string') { setAnswer(parsedContent.answer); } // Check for images array if (parsedContent.images && Array.isArray(parsedContent.images)) { setImages(parsedContent.images); } } catch (e) { // Silently fail - the view will work without these extras } } }, [toolContent]); // Helper to determine favicon const getFavicon = (url: string) => { try { const domain = new URL(url).hostname; return `https://www.google.com/s2/favicons?domain=${domain}&sz=128`; } catch (e) { return null; } }; const getResultType = (result: any) => { const { url, title } = result; if (url.includes('news') || url.includes('article') || title.includes('News')) { return { icon: FileText, label: 'Article' }; } else if (url.includes('wiki')) { return { icon: BookOpen, label: 'Wiki' }; } else if (url.includes('blog')) { return { icon: CalendarDays, label: 'Blog' }; } else { return { icon: Globe, label: 'Website' }; } }; return (
{toolTitle}
{!isStreaming && ( {isSuccess ? ( ) : ( )} {isSuccess ? 'Search completed successfully' : 'Search failed'} )}
{isStreaming ? (

Searching the web

{query}

{progress}%

) : searchResults.length > 0 ? (
{answer && (

Answer

{answer}

)} {images.length > 0 && ( )}
Search Results ({searchResults.length}) {new Date().toLocaleDateString()}
{searchResults.map((result, idx) => { const { icon: ResultTypeIcon, label: resultTypeLabel } = getResultType(result); const isExpanded = expandedResults[idx] || false; const favicon = getFavicon(result.url); return (
{favicon && ( { (e.target as HTMLImageElement).style.display = 'none'; }} /> )}
{resultTypeLabel}
{result.title}
{truncateText(cleanUrl(result.url))}

{isExpanded ? 'Show less' : 'Show more'}

{result.snippet && (

{result.snippet}

)}
{isExpanded && (
Source: {cleanUrl(result.url)}
)}
); })}
) : (

No Results Found

{query || 'Unknown query'}

Try refining your search query for better results

)}
{!isStreaming && searchResults.length > 0 && ( {searchResults.length} results )}
{toolTimestamp && !isStreaming ? formatTimestamp(toolTimestamp) : assistantTimestamp ? formatTimestamp(assistantTimestamp) : ''}
); }