import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:markdown/markdown.dart' as md; class CodeBlockBuilder extends MarkdownElementBuilder { @override Widget? visitElementAfter(md.Element element, TextStyle? preferredStyle) { final code = element.textContent; final language = element.attributes['class']?.replaceFirst('language-', '') ?? ''; return CodeBlockWidget( code: code, language: language, ); } } class CodeBlockWidget extends StatelessWidget { final String code; final String language; const CodeBlockWidget({ super.key, required this.code, this.language = '', }); @override Widget build(BuildContext context) { return Container( margin: const EdgeInsets.symmetric(vertical: 8), decoration: BoxDecoration( color: const Color(0xFF0D1117), borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.grey.shade800), ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Header with language and copy button Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: Colors.grey.shade900, borderRadius: const BorderRadius.only( topLeft: Radius.circular(8), topRight: Radius.circular(8), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( language.isNotEmpty ? language : 'code', style: TextStyle( fontSize: 12, color: Colors.grey.shade400, fontFamily: 'JetBrainsMono', ), ), InkWell( onTap: () { Clipboard.setData(ClipboardData(text: code)); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Code copied'), duration: Duration(seconds: 1), ), ); }, child: Padding( padding: const EdgeInsets.all(4), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.copy, size: 14, color: Colors.grey.shade400), const SizedBox(width: 4), Text( 'Copy', style: TextStyle( fontSize: 12, color: Colors.grey.shade400, ), ), ], ), ), ), ], ), ), // Code content SingleChildScrollView( scrollDirection: Axis.horizontal, padding: const EdgeInsets.all(12), child: SelectableText( code.trimRight(), style: const TextStyle( fontFamily: 'JetBrainsMono', fontSize: 13, color: Color(0xFFE6EDF3), height: 1.5, ), ), ), ], ), ); } }