"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 ( ); } // 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 && ( )}
{/* Footer: exit code */} {exitCode !== null && (
Exit code {exitCode}
)}
) : null}
); }