"use client"; import { useState } from "react"; import { Copy, Check, Clock } from "lucide-react"; import { cn } from "@/lib/utils"; import { AnsiRenderer } from "./AnsiRenderer"; import { ToolUseBlock } from "./ToolUseBlock"; interface ToolBashProps { input: { command: string; timeout?: number; description?: string; }; result?: string; isError?: boolean; isRunning?: boolean; startedAt?: number; completedAt?: number; } function CopyButton({ text }: { text: string }) { const [copied, setCopied] = useState(false); return ( { navigator.clipboard.writeText(text).then(() => { setCopied(true); setTimeout(() => setCopied(false), 2000); }); }} className="p-1 rounded text-surface-400 hover:text-surface-200 hover:bg-surface-700 transition-colors" title="Copy command" > {copied ? ( ) : ( )} ); } // Parse exit code from result — bash tool often appends it function parseExitCode(result: string): number | null { const match = result.match(/\nExit code: (\d+)\s*$/); if (match) return parseInt(match[1], 10); return null; } function stripExitCode(result: string): string { return result.replace(/\nExit code: \d+\s*$/, ""); } const MAX_OUTPUT_LINES = 200; export function ToolBash({ input, result, isError = false, isRunning = false, startedAt, completedAt, }: ToolBashProps) { const [showAll, setShowAll] = useState(false); const exitCode = result ? parseExitCode(result) : null; const outputText = result ? stripExitCode(result) : ""; const outputLines = outputText.split("\n"); const isTruncated = !showAll && outputLines.length > MAX_OUTPUT_LINES; const displayOutput = isTruncated ? outputLines.slice(0, MAX_OUTPUT_LINES).join("\n") : outputText; // Determine if it's an error (non-zero exit code or isError prop) const hasError = isError || (exitCode !== null && exitCode !== 0); return ( {/* Command display */} $ {input.command} {input.timeout && ( {(input.timeout / 1000).toFixed(0)}s )} {/* Output */} {isRunning ? ( Running… ) : outputText ? ( {isTruncated && ( setShowAll(true)} className="mt-2 block text-brand-400 hover:text-brand-300 text-xs" > ↓ Show {outputLines.length - MAX_OUTPUT_LINES} more lines )} {/* Footer: exit code */} {exitCode !== null && ( Exit code {exitCode} )} ) : null} ); }
{input.command}