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