({
toolName,
toolInput: _toolInput,
toolResult: _toolResult,
isError = false,
isRunning = false,
startedAt,
completedAt,
children,
defaultExpanded = false,
}: ToolUseBlockProps)
| 139 | } |
| 140 | |
| 141 | export function ToolUseBlock({ |
| 142 | toolName, |
| 143 | toolInput: _toolInput, |
| 144 | toolResult: _toolResult, |
| 145 | isError = false, |
| 146 | isRunning = false, |
| 147 | startedAt, |
| 148 | completedAt, |
| 149 | children, |
| 150 | defaultExpanded = false, |
| 151 | }: ToolUseBlockProps) { |
| 152 | const [isExpanded, setIsExpanded] = useState(defaultExpanded || isRunning); |
| 153 | const startRef = useRef(startedAt ?? Date.now()); |
| 154 | |
| 155 | // Auto-expand while running, retain state after completion |
| 156 | useEffect(() => { |
| 157 | if (isRunning) setIsExpanded(true); |
| 158 | }, [isRunning]); |
| 159 | |
| 160 | const Icon = getToolIcon(toolName); |
| 161 | const label = getToolLabel(toolName); |
| 162 | |
| 163 | const borderColor = isRunning |
| 164 | ? "border-brand-600/40" |
| 165 | : isError |
| 166 | ? "border-red-800/50" |
| 167 | : "border-surface-700"; |
| 168 | |
| 169 | const headerBg = isRunning |
| 170 | ? "bg-brand-950/30" |
| 171 | : isError |
| 172 | ? "bg-red-950/20" |
| 173 | : "bg-surface-850"; |
| 174 | |
| 175 | return ( |
| 176 | <div |
| 177 | className={cn( |
| 178 | "rounded-lg border overflow-hidden text-sm", |
| 179 | borderColor |
| 180 | )} |
| 181 | > |
| 182 | {/* Header row */} |
| 183 | <button |
| 184 | onClick={() => setIsExpanded((v) => !v)} |
| 185 | className={cn( |
| 186 | "w-full flex items-center gap-2 px-3 py-2.5 text-left transition-colors", |
| 187 | headerBg, |
| 188 | "hover:bg-surface-800" |
| 189 | )} |
| 190 | > |
| 191 | {/* Expand icon */} |
| 192 | <span className="text-surface-500 flex-shrink-0"> |
| 193 | {isExpanded ? ( |
| 194 | <ChevronDown className="w-3.5 h-3.5" /> |
| 195 | ) : ( |
| 196 | <ChevronRight className="w-3.5 h-3.5" /> |
| 197 | )} |
| 198 | </span> |
nothing calls this directly
no test coverage detected