Files
captain-mobile/lib/widgets/code_block.dart
ARCHITECT 3663e4c622 Initial commit: Captain Claude Mobile App
- Flutter app with chat and terminal screens
- WebSocket integration for real-time chat
- xterm integration for screen sessions
- Markdown rendering with code blocks
- JWT authentication

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-16 18:34:02 +00:00

112 lines
3.5 KiB
Dart

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,
),
),
),
],
),
);
}
}