import 'package:flutter/material.dart'; import '../services/ai_service.dart'; typedef TextUpdateCallback = void Function(String updatedText); typedef ProcessingStateCallback = void Function(bool isProcessing); /// An AI icon button that immediately enhances the text in [textController] /// when pressed — no dialog, no language detection. /// /// Provide [promptBuilder] to give the AI field-specific context. It receives /// the current (trimmed) text and must return the prompt instruction string. class GeminiButton extends StatefulWidget { final TextEditingController textController; final TextUpdateCallback onTextUpdated; final ProcessingStateCallback? onProcessingStateChanged; final String? tooltip; /// Optional callback that builds the Gemini prompt instruction from the /// current field text (called at press time, so captures live context). final String Function(String text)? promptBuilder; /// Called when the active AI provider changes. /// [isDeepSeek] is true once Gemini fails and DeepSeek takes over. /// Called with false again when processing finishes. final void Function(bool isDeepSeek)? onProviderChanged; const GeminiButton({ super.key, required this.textController, required this.onTextUpdated, this.onProcessingStateChanged, this.tooltip, this.promptBuilder, this.onProviderChanged, }); @override State createState() => _GeminiButtonState(); } class _GeminiButtonState extends State { bool _isProcessing = false; late final AiService _aiService; @override void initState() { super.initState(); _aiService = AiService(); } Future _enhance(BuildContext context) async { final text = widget.textController.text.trim(); if (text.isEmpty) { if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Please enter some text first')), ); return; } setState(() { _isProcessing = true; }); widget.onProcessingStateChanged?.call(true); widget.onProviderChanged?.call(false); try { final instruction = widget.promptBuilder?.call(text); final improvedText = await _aiService.enhanceText( text, promptInstruction: instruction, onFallbackToDeepSeek: () { if (mounted) { widget.onProviderChanged?.call(true); } }, ); final trimmed = improvedText.trim(); widget.textController.text = trimmed; widget.onTextUpdated(trimmed); if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Text improved successfully')), ); } catch (e) { if (!context.mounted) return; ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('Error: $e'))); } finally { if (mounted) { setState(() { _isProcessing = false; }); } widget.onProcessingStateChanged?.call(false); widget.onProviderChanged?.call(false); } } @override Widget build(BuildContext context) { return IconButton( tooltip: widget.tooltip ?? 'Improve with Gemini', icon: _isProcessing ? const SizedBox( width: 24, height: 24, child: CircularProgressIndicator(strokeWidth: 2), ) : Image.asset( 'gemini_icon.png', width: 24, height: 24, errorBuilder: (context, error, stackTrace) => const Icon(Icons.auto_awesome), ), onPressed: _isProcessing ? null : () => _enhance(context), ); } }