1import React, { useState } from "react";
2import { LLMContent } from "../types";
3
4interface BrowserEvalToolProps {
5 // For tool_use (pending state)
6 toolInput?: unknown; // { expression: string }
7 isRunning?: boolean;
8
9 // For tool_result (completed state)
10 toolResult?: LLMContent[];
11 hasError?: boolean;
12 executionTime?: string;
13}
14
15function BrowserEvalTool({
16 toolInput,
17 isRunning,
18 toolResult,
19 hasError,
20 executionTime,
21}: BrowserEvalToolProps) {
22 const [isExpanded, setIsExpanded] = useState(false);
23
24 // Extract expression from toolInput
25 const expression =
26 typeof toolInput === "object" &&
27 toolInput !== null &&
28 "expression" in toolInput &&
29 typeof (toolInput as { expression?: unknown }).expression === "string"
30 ? (toolInput as { expression: string }).expression
31 : typeof toolInput === "string"
32 ? toolInput
33 : "";
34
35 // Extract result from toolResult
36 const result =
37 toolResult && toolResult.length > 0 && toolResult[0].Text ? toolResult[0].Text : "";
38
39 // Truncate expression for display
40 const truncateText = (text: string, maxLen: number = 300) => {
41 if (text.length <= maxLen) return text;
42 return text.substring(0, maxLen) + "...";
43 };
44
45 const displayExpression = truncateText(expression);
46 const isComplete = !isRunning && toolResult !== undefined;
47
48 return (
49 <div className="tool" data-testid={isComplete ? "tool-call-completed" : "tool-call-running"}>
50 <div className="tool-header" onClick={() => setIsExpanded(!isExpanded)}>
51 <div className="tool-summary">
52 <span className={`tool-emoji ${isRunning ? "running" : ""}`}>⚡</span>
53 <span className="tool-command">{displayExpression}</span>
54 {isComplete && hasError && <span className="tool-error">✗</span>}
55 {isComplete && !hasError && <span className="tool-success">✓</span>}
56 </div>
57 <button
58 className="tool-toggle"
59 aria-label={isExpanded ? "Collapse" : "Expand"}
60 aria-expanded={isExpanded}
61 >
62 <svg
63 width="12"
64 height="12"
65 viewBox="0 0 12 12"
66 fill="none"
67 xmlns="http://www.w3.org/2000/svg"
68 style={{
69 transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)",
70 transition: "transform 0.2s",
71 }}
72 >
73 <path
74 d="M4.5 3L7.5 6L4.5 9"
75 stroke="currentColor"
76 strokeWidth="1.5"
77 strokeLinecap="round"
78 strokeLinejoin="round"
79 />
80 </svg>
81 </button>
82 </div>
83
84 {isExpanded && (
85 <div className="tool-details">
86 <div className="tool-section">
87 <div className="tool-label">Expression:</div>
88 <pre className="tool-code">{expression}</pre>
89 </div>
90
91 {isComplete && (
92 <div className="tool-section">
93 <div className="tool-label">
94 Result{hasError ? " (Error)" : ""}:
95 {executionTime && <span className="tool-time">{executionTime}</span>}
96 </div>
97 <pre className={`tool-code ${hasError ? "error" : ""}`}>
98 {result || "(no result)"}
99 </pre>
100 </div>
101 )}
102 </div>
103 )}
104 </div>
105 );
106}
107
108export default BrowserEvalTool;