import React, { useState, useEffect } from 'react'; import { Globe, CheckCircle, AlertTriangle, Loader2, FileText, Copy, Check, ArrowUpRight, BookOpen } from 'lucide-react'; import { ToolViewProps } from './types'; import { extractCrawlUrl, extractWebpageContent, formatTimestamp, getToolTitle, extractToolData, } from './utils'; import { cn, truncateString } from '@/lib/utils'; import { useTheme } from 'next-themes'; import { Card, CardContent, CardHeader, CardTitle } 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"; export function WebCrawlToolView({ name = 'crawl-webpage', assistantContent, toolContent, assistantTimestamp, toolTimestamp, isSuccess = true, isStreaming = false, }: ToolViewProps) { const { resolvedTheme } = useTheme(); const [progress, setProgress] = useState(0); const [copiedContent, setCopiedContent] = useState(false); // Try to extract data using the new parser first const assistantToolData = extractToolData(assistantContent); const toolToolData = extractToolData(toolContent); let url: string | null = null; // Use data from the new format if available if (assistantToolData.toolResult) { url = assistantToolData.url; } else if (toolToolData.toolResult) { url = toolToolData.url; } // If not found in new format, fall back to legacy extraction if (!url) { url = extractCrawlUrl(assistantContent); } const webpageContent = extractWebpageContent(toolContent); const toolTitle = getToolTitle(name); // Format domain for display const formatDomain = (url: string): string => { try { const urlObj = new URL(url); return urlObj.hostname.replace('www.', ''); } catch (e) { return url; } }; const domain = url ? formatDomain(url) : 'Unknown'; // Get 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 favicon = url ? getFavicon(url) : null; // 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]); const copyContent = async () => { if (!webpageContent?.text) return; try { await navigator.clipboard.writeText(webpageContent.text); setCopiedContent(true); setTimeout(() => setCopiedContent(false), 2000); } catch (err) { console.error('Failed to copy:', err); } }; const getContentStats = (content: string) => { const wordCount = content.trim().split(/\s+/).length; const charCount = content.length; const lineCount = content.split('\n').length; return { wordCount, charCount, lineCount }; }; const contentStats = webpageContent?.text ? getContentStats(webpageContent.text) : null; return (
{toolTitle}
{!isStreaming && ( {isSuccess ? ( ) : ( )} {isSuccess ? 'Crawling completed' : 'Crawling failed'} )}
{isStreaming ? (

Crawling Webpage

Fetching content from {domain}

{progress}% complete

) : url ? ( // Results State
{/* Target URL Section */}
Source URL
{favicon && ( { (e.target as HTMLImageElement).style.display = 'none'; }} /> )}

{truncateString(url, 70)}

{domain}

{/* Content Section */}
Extracted Content
{contentStats && (
{contentStats.wordCount} words {contentStats.charCount} chars
)}
{webpageContent?.text ? (
{/* Content Header */}

Page Content

{contentStats && (

{contentStats.lineCount} lines

)}

{copiedContent ? 'Copied!' : 'Copy content'}

{/* Content Body */}
                        {webpageContent.text}
                      
) : (

No Content Extracted

The webpage might be restricted, empty, or require JavaScript to load content

)}
) : (

No URL Detected

Unable to extract a valid URL from the crawling request

)}
{/* Footer */}
{!isStreaming && webpageContent?.text && (
Content extracted )}
{toolTimestamp && !isStreaming ? formatTimestamp(toolTimestamp) : assistantTimestamp ? formatTimestamp(assistantTimestamp) : ''}
); }