import 'dart:math' show min; import 'package:flutter/material.dart'; import 'package:flutter_markdown_plus/flutter_markdown_plus.dart'; import '../data/models/message.dart'; class ChatMessageBubble extends StatelessWidget { final Message message; final String streamingStatus; const ChatMessageBubble({ super.key, required this.message, this.streamingStatus = '', }); @override Widget build(BuildContext context) { final isUser = message.role == MessageRole.user; final scheme = Theme.of(context).colorScheme; final isGenerating = message.status == 'generating'; return Align( alignment: isUser ? Alignment.centerRight : Alignment.centerLeft, child: Container( constraints: BoxConstraints( maxWidth: min(MediaQuery.of(context).size.width * 0.82, 480), ), margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 4), decoration: isUser ? BoxDecoration( // Ghost style: transparent bg, thin border color: Colors.transparent, border: Border.all( color: scheme.primary.withValues(alpha: 0.35), width: 1, ), borderRadius: const BorderRadius.only( topLeft: Radius.circular(16), topRight: Radius.circular(16), bottomLeft: Radius.circular(16), bottomRight: Radius.circular(4), ), ) : BoxDecoration( // Assistant: elevated surface + left accent border color: scheme.surfaceContainerHighest, border: Border( left: BorderSide(color: scheme.primary, width: 2), ), borderRadius: const BorderRadius.only( topLeft: Radius.circular(4), topRight: Radius.circular(16), bottomLeft: Radius.circular(4), bottomRight: Radius.circular(16), ), ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: isGenerating && message.content.isEmpty ? Row( mainAxisSize: MainAxisSize.min, children: [ SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, color: scheme.onSurfaceVariant, ), ), if (streamingStatus.isNotEmpty) ...[ const SizedBox(width: 8), Flexible( child: Text( streamingStatus, style: TextStyle( fontSize: 12, color: scheme.onSurfaceVariant, fontStyle: FontStyle.italic, ), ), ), ], ], ) : MarkdownBody( data: message.content.isEmpty ? '…' : message.content, styleSheet: MarkdownStyleSheet( p: TextStyle( color: isUser ? scheme.onSurface.withValues(alpha: 0.75) : scheme.onSurface, fontSize: 14, ), ), ), ), ), ); } }