The-Agency/lib/ui/widgets/message_bubble.dart

94 lines
2.9 KiB
Dart

import "package:flutter/material.dart" as material hide Card;
import "package:flutter_markdown/flutter_markdown.dart";
import "package:shadcn_flutter/shadcn_flutter.dart";
import "../../src/session/session_types.dart";
class MessageBubble extends StatelessWidget {
const MessageBubble({required this.message});
final Message message;
@override
Widget build(BuildContext context) {
final isUser = message.role == "user";
final isTool = message.role == "tool";
final isAssistant = message.role == "assistant";
final accentColor = isTool
? const Color(0xFF64748B)
: const Color(0xFF94A3B8);
return Align(
alignment: isUser ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
constraints: BoxConstraints(
maxWidth: material.MediaQuery.of(context).size.width * 0.7,
),
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Card(
child: Padding(
padding: const EdgeInsets.all(12),
child: material.Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
message.role,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: accentColor,
),
),
const SizedBox(height: 4),
if (isAssistant || isTool)
MarkdownBody(
data: isTool
? _buildToolMarkdown(message.content)
: message.content,
selectable: true,
shrinkWrap: true,
styleSheet: isTool
? _toolMarkdownStyleSheet(context)
: null,
)
else
Text(message.content),
],
),
),
),
),
);
}
String _buildToolMarkdown(String content) {
final lines = content.split("\n");
if (lines.isEmpty) {
return "```text\n\n```";
}
final title = lines.first.trim();
final body = lines.skip(1).join("\n").trimRight();
if (body.isEmpty) {
return title;
}
return "$title\n\n```text\n$body\n```";
}
MarkdownStyleSheet _toolMarkdownStyleSheet(BuildContext context) {
final theme = Theme.of(context);
return MarkdownStyleSheet.fromTheme(material.Theme.of(context)).copyWith(
p: theme.typography.base.copyWith(height: 1.35),
codeblockDecoration: BoxDecoration(
color: theme.colorScheme.muted.withValues(alpha: 0.35),
borderRadius: BorderRadius.circular(10),
),
codeblockPadding: const EdgeInsets.all(12),
code: theme.typography.base.copyWith(
fontFamily: "monospace",
height: 1.35,
),
);
}
}