"use client"; import { FileIcon } from "./FileIcon"; import { cn } from "@/lib/utils"; import { ToolUseBlock } from "./ToolUseBlock"; interface ToolGrepProps { input: { pattern: string; path?: string; glob?: string; type?: string; output_mode?: string; "-i"?: boolean; "-n"?: boolean; context?: number; "-A"?: number; "-B"?: number; "-C"?: number; }; result?: string; isError?: boolean; isRunning?: boolean; startedAt?: number; completedAt?: number; } interface GrepMatch { file: string; lineNo?: number; content: string; isContext?: boolean; } interface GrepGroup { file: string; matches: GrepMatch[]; } function parseGrepOutput(result: string): GrepGroup[] { const lines = result.split("\n").filter(Boolean); const groups: Map = new Map(); for (const line of lines) { // Format: "file:lineNo:content" or "file:content" or just "file" const colonMatch = line.match(/^([^:]+):(\d+):(.*)$/); if (colonMatch) { const [, file, lineNo, content] = colonMatch; if (!groups.has(file)) groups.set(file, []); groups.get(file)!.push({ file, lineNo: parseInt(lineNo, 10), content }); } else if (line.match(/^[^:]+$/)) { // Files-only mode if (!groups.has(line)) groups.set(line, []); } else { // fallback: treat entire line as content with unknown file if (!groups.has("")) groups.set("", []); groups.get("")!.push({ file: "", content: line }); } } return Array.from(groups.entries()).map(([file, matches]) => ({ file, matches, })); } function highlightPattern(text: string, pattern: string): React.ReactNode { try { const re = new RegExp(`(${pattern})`, "gi"); const parts = text.split(re); return parts.map((part, i) => re.test(part) ? ( {part} ) : ( part ) ); } catch { return text; } } export function ToolGrep({ input, result, isError = false, isRunning = false, startedAt, completedAt, }: ToolGrepProps) { const groups = result ? parseGrepOutput(result) : []; const totalMatches = groups.reduce((sum, g) => sum + g.matches.length, 0); const flags = [ input["-i"] && "-i", input["-n"] !== false && "-n", input.glob && `--glob ${input.glob}`, input.type && `--type ${input.type}`, input.context && `-C ${input.context}`, ] .filter(Boolean) .join(" "); return ( {/* Search header */}
{input.pattern} {flags && {flags}} {input.path && ( in {input.path} )} {!isRunning && totalMatches > 0 && ( {totalMatches} match{totalMatches !== 1 ? "es" : ""} )}
{/* Results */} {isRunning ? (
Searching…
) : isError ? (
{result}
) : groups.length === 0 ? (
No matches found.
) : (
{groups.map((group, gi) => (
{/* File header */}
{group.file || "(unknown)"} {group.matches.length} match{group.matches.length !== 1 ? "es" : ""}
{/* Match lines */} {group.matches.map((match, mi) => (
{match.lineNo !== undefined && ( {match.lineNo} )} {highlightPattern(match.content, input.pattern)}
))}
))}
)}
); }