/** * @license * Copyright 2025 Qwen Team * SPDX-License-Identifier: Apache-2.0 * * Search tool call component - specialized for search operations */ import type React from 'react'; import type { BaseToolCallProps } from '../shared/types.js'; import { FileLink } from '../../ui/FileLink.js'; import { safeTitle, groupContent, mapToolStatusToContainerStatus, } from '../shared/utils.js'; /** * Specialized component for Search tool calls * Optimized for displaying search operations and results * Shows query + result count or file list */ // Local, scoped inline container for compact search rows (single result/text-only) const InlineContainer: React.FC<{ status: 'success' | 'error' | 'warning' | 'loading' | 'default'; labelSuffix?: string; children?: React.ReactNode; isFirst?: boolean; isLast?: boolean; }> = ({ status, labelSuffix, children, isFirst, isLast }) => { const beforeStatusClass = status === 'success' ? 'before:text-qwen-success' : status === 'error' ? 'before:text-qwen-error' : status === 'warning' ? 'before:text-qwen-warning' : 'before:text-qwen-loading before:opacity-70 before:animate-pulse-slow'; const lineCropTop = isFirst ? 'top-[24px]' : 'top-0'; const lineCropBottom = isLast ? 'bottom-auto h-[calc(100%-24px)]' : 'bottom-0'; return (
{/* timeline vertical line */}
Search {labelSuffix ? ( {labelSuffix} ) : null}
{children ? (
{children}
) : null}
); }; // Local card layout for multi-result or error display const SearchCard: React.FC<{ status: 'success' | 'error' | 'warning' | 'loading' | 'default'; children: React.ReactNode; isFirst?: boolean; isLast?: boolean; }> = ({ status, children, isFirst, isLast }) => { const beforeStatusClass = status === 'success' ? 'before:text-qwen-success' : status === 'error' ? 'before:text-qwen-error' : status === 'warning' ? 'before:text-qwen-warning' : 'before:text-qwen-loading before:opacity-70 before:animate-pulse-slow'; const lineCropTop = isFirst ? 'top-[24px]' : 'top-0'; const lineCropBottom = isLast ? 'bottom-auto h-[calc(100%-24px)]' : 'bottom-0'; return (
{/* timeline vertical line */}
{children}
); }; const SearchRow: React.FC<{ label: string; children: React.ReactNode }> = ({ label, children, }) => (
{label}
{children}
); const LocationsListLocal: React.FC<{ locations: Array<{ path: string; line?: number | null }>; }> = ({ locations }) => (
{locations.map((loc, idx) => ( ))}
); export const SearchToolCall: React.FC = ({ toolCall, isFirst, isLast, }) => { const { title, content, locations } = toolCall; const queryText = safeTitle(title); // Group content by type const { errors, textOutputs } = groupContent(content); // Error case: show search query + error in card layout if (errors.length > 0) { return (
{queryText}
{errors.join('\n')}
); } // Success case with results: show search query + file list if (locations && locations.length > 0) { const containerStatus = mapToolStatusToContainerStatus(toolCall.status); // If multiple results, use card layout; otherwise use compact format if (locations.length > 1) { return (
{queryText}
); } // Single result - compact format return ( ); } // Show content text if available (e.g., "Listed 4 item(s).") if (textOutputs.length > 0) { const containerStatus = mapToolStatusToContainerStatus(toolCall.status); return (
{textOutputs.map((text, index) => (
{text}
))}
); } // No results - show query only if (queryText) { const containerStatus = mapToolStatusToContainerStatus(toolCall.status); return ( {queryText} ); } return null; };