diff --git a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_generate_codeblock.dart b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_generate_codeblock.dart index 34ca1dd6..103fd356 100644 --- a/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_generate_codeblock.dart +++ b/lib/dashbot/core/common/widgets/dashbot_action_buttons/dashbot_generate_codeblock.dart @@ -1,32 +1,95 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:apidash_design_system/apidash_design_system.dart'; import '../../../../features/chat/models/chat_action.dart'; import '../dashbot_action.dart'; -class DashbotGeneratedCodeBlock extends StatelessWidget - with DashbotActionMixin { +class DashbotGeneratedCodeBlock extends StatefulWidget with DashbotActionMixin { @override final ChatAction action; const DashbotGeneratedCodeBlock({super.key, required this.action}); + @override + State createState() => + _DashbotGeneratedCodeBlockState(); +} + +class _DashbotGeneratedCodeBlockState extends State { + bool _isCopied = false; + + Future _copyCode(String code) async { + await Clipboard.setData(ClipboardData(text: code)); + setState(() { + _isCopied = true; + }); + + // Reset the icon back to copy after 1.5 seconds + Future.delayed(const Duration(milliseconds: 1500), () { + if (mounted) { + setState(() { + _isCopied = false; + }); + } + }); + } + @override Widget build(BuildContext context) { - final code = (action.value is String) ? action.value as String : ''; + final code = + (widget.action.value is String) ? widget.action.value as String : ''; + final isDark = Theme.of(context).brightness == Brightness.dark; + final codeTheme = isDark ? kDarkCodeTheme : kLightCodeTheme; + return Container( width: double.infinity, - padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, + color: codeTheme['root']?.backgroundColor ?? + Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(8), border: Border.all( color: Theme.of(context).colorScheme.outlineVariant, ), ), - child: SelectableText( - code.isEmpty ? '// No code returned' : code, - style: Theme.of(context).textTheme.bodySmall?.copyWith( - fontFamily: 'monospace', + child: Stack( + children: [ + GestureDetector( + onTap: code.isNotEmpty ? () => _copyCode(code) : null, + child: Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + child: Text( + code.isEmpty ? '// No code returned' : code, + style: kCodeStyle.copyWith( + fontSize: Theme.of(context).textTheme.bodySmall?.fontSize, + color: codeTheme['root']?.color ?? + Theme.of(context).colorScheme.onSurface, + ), + ), ), + ), + if (code.isNotEmpty) + Positioned( + top: 8, + right: 8, + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: ADIconButton( + key: ValueKey(_isCopied), + icon: _isCopied ? Icons.check : Icons.content_copy, + iconSize: 16, + tooltip: _isCopied ? 'Copied!' : 'Copy', + color: _isCopied + ? Theme.of(context).colorScheme.primary + : (codeTheme['root']?.color ?? + Theme.of(context).colorScheme.onSurface) + .withValues(alpha: 0.6), + visualDensity: VisualDensity.compact, + onPressed: () => _copyCode(code), + ), + ), + ), + ], ), ); }