/**
* @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 */}
);
};
const SearchRow: React.FC<{ label: string; children: React.ReactNode }> = ({
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;
};