import 'dart:convert'; import 'package:flutter/material.dart'; import '../models/message.dart'; class ToolUseCard extends StatefulWidget { final ToolUse toolUse; const ToolUseCard({super.key, required this.toolUse}); @override State createState() => _ToolUseCardState(); } class _ToolUseCardState extends State { bool _isExpanded = false; IconData get _toolIcon { switch (widget.toolUse.tool.toLowerCase()) { case 'bash': return Icons.terminal; case 'read': return Icons.description; case 'write': case 'edit': return Icons.edit_document; case 'grep': case 'glob': return Icons.search; default: return Icons.build; } } String get _inputDisplay { final input = widget.toolUse.input; if (input == null) return ''; if (input is String) return input; if (input is Map) { // For Bash tool, show command if (input.containsKey('command')) { return input['command']; } // For Read tool, show file path if (input.containsKey('file_path')) { return input['file_path']; } // Otherwise show JSON try { return const JsonEncoder.withIndent(' ').convert(input); } catch (_) { return input.toString(); } } return input.toString(); } @override Widget build(BuildContext context) { final hasOutput = widget.toolUse.output != null && widget.toolUse.output!.isNotEmpty; return Container( margin: const EdgeInsets.symmetric(vertical: 4), decoration: BoxDecoration( color: const Color(0xFF2D2D2D), borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.orange.shade700.withOpacity(0.3)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Header InkWell( onTap: hasOutput ? () => setState(() => _isExpanded = !_isExpanded) : null, child: Padding( padding: const EdgeInsets.all(12), child: Row( children: [ Icon(_toolIcon, size: 18, color: Colors.orange.shade400), const SizedBox(width: 8), Text( widget.toolUse.tool, style: TextStyle( color: Colors.orange.shade400, fontWeight: FontWeight.bold, fontSize: 13, ), ), const Spacer(), if (hasOutput) Icon( _isExpanded ? Icons.expand_less : Icons.expand_more, color: Colors.grey.shade500, size: 20, ), if (!hasOutput && widget.toolUse.output == null) SizedBox( width: 14, height: 14, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.orange.shade400, ), ), ], ), ), ), // Input/Command if (_inputDisplay.isNotEmpty) Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: Colors.black.withOpacity(0.2), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '▶', style: TextStyle( color: Colors.green.shade400, fontFamily: 'JetBrainsMono', fontSize: 12, ), ), const SizedBox(width: 8), Expanded( child: Text( _inputDisplay, style: const TextStyle( color: Colors.white70, fontFamily: 'JetBrainsMono', fontSize: 12, ), maxLines: 3, overflow: TextOverflow.ellipsis, ), ), ], ), ), // Output (collapsible) if (_isExpanded && hasOutput) Container( constraints: const BoxConstraints(maxHeight: 200), padding: const EdgeInsets.all(12), child: SingleChildScrollView( child: Text( widget.toolUse.output!, style: const TextStyle( color: Colors.white60, fontFamily: 'JetBrainsMono', fontSize: 11, ), ), ), ), ], ), ); } }