'use client' import { UseChatHelpers } from '@ai-sdk/react' import { IconCheck as Check, IconSearch as SearchIcon } from '@tabler/icons-react' import { toPublicErrorPayload } from '@/lib/errors/public-error' import type { SearchResults as TypeSearchResults } from '@/lib/types' import type { ToolPart, UIDataTypes, UIMessage, UITools } from '@/lib/types/ai' import { cn } from '@/lib/utils' import { useArtifact } from '@/components/artifact/artifact-context' import { StatusIndicator } from './ui/status-indicator' import { CollapsibleMessage } from './collapsible-message' import { SearchSkeleton } from './default-skeleton' import ProcessHeader from './process-header' import { SearchResults } from './search-results' import { SearchResultsImageSection } from './search-results-image' import { Section } from './section' import { SourceFavicons } from './source-favicons' import { createVideoSearchResults, VideoSearchResults } from './video-search-results' interface SearchSectionProps { tool: ToolPart<'search'> isOpen: boolean onOpenChange: (open: boolean) => void status?: UseChatHelpers>['status'] borderless?: boolean isFirst?: boolean isLast?: boolean } export function SearchSection({ tool, isOpen, onOpenChange, status, borderless, isFirst = false, isLast = false }: SearchSectionProps) { const isLoading = status === 'submitted' || status === 'streaming' const isToolLoading = tool.state === 'input-streaming' || tool.state === 'input-available' // Handle streaming output states const output = tool.state === 'output-available' ? tool.output : undefined const isSearching = output?.state === 'searching' const searchResults: TypeSearchResults | undefined = output?.state === 'complete' ? output : undefined const isError = tool.state === 'output-error' const errorMessage = toPublicErrorPayload(tool.errorText, { fallbackMessage: 'Search failed' }).error const query = tool.input?.query || output?.query || '' const includeDomains = tool.input?.include_domains const includeDomainsString = Array.isArray(includeDomains) && includeDomains.length > 0 ? ` [${includeDomains.join(', ')}]` : '' const { open } = useArtifact() const totalResults = (searchResults?.results?.length || 0) + (searchResults?.videos?.length || 0) + (searchResults?.images?.length || 0) const header = ( open(tool)} isLoading={isLoading && (isToolLoading || isSearching)} ariaExpanded={isOpen} label={
{`${query}${includeDomainsString}`}
} meta={ searchResults && totalResults > 0 ? (
{totalResults} results {searchResults.results && searchResults.results.length > 0 && ( )}
) : undefined } /> ) return (
{/* Rails for header - show based on position */} {borderless && ( <> {!isFirst && (
)} {!isLast && (
)} )}
{/* Rail space - always reserved when grouped */} {borderless && ( <>
)}
{searchResults && searchResults.images && searchResults.images.length > 0 && (
)} {searchResults && searchResults.videos && searchResults.videos.length > 0 && (
)} {isError ? (
{errorMessage}
) : (isLoading && isToolLoading) || isSearching ? ( ) : searchResults?.results && searchResults.results.length > 0 ? (
) : null}
) }