import 'package:flutter/material.dart'; import '../../../shared/theme/theme_extensions.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:url_launcher/url_launcher_string.dart'; import '../../../core/widgets/error_boundary.dart'; import '../../../shared/widgets/improved_loading_states.dart'; import '../../../shared/utils/ui_utils.dart'; import '../../../shared/widgets/conduit_components.dart'; import '../../../core/providers/app_providers.dart'; import '../../auth/providers/unified_auth_providers.dart'; import '../../../core/services/settings_service.dart'; import '../../../core/models/model.dart'; import 'dart:async'; import 'dart:io'; import '../../chat/views/chat_page_helpers.dart'; /// Profile page (You tab) showing user info and main actions /// Enhanced with production-grade design tokens for better cohesion class ProfilePage extends ConsumerWidget { const ProfilePage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final user = ref.watch(currentUserProvider); return ErrorBoundary( child: user.when( data: (userData) => Scaffold( backgroundColor: context.conduitTheme.surfaceBackground, appBar: AppBar( backgroundColor: context.conduitTheme.surfaceBackground, elevation: Elevation.none, automaticallyImplyLeading: false, leading: IconButton( icon: Icon( UiUtils.platformIcon( ios: CupertinoIcons.back, android: Icons.arrow_back, ), color: context.conduitTheme.textPrimary, ), onPressed: () => Navigator.of(context).maybePop(), tooltip: 'Back', ), toolbarHeight: kToolbarHeight, titleSpacing: 0.0, title: Row( mainAxisSize: MainAxisSize.min, children: [ Text( 'You', style: context.conduitTheme.headingSmall?.copyWith( color: context.conduitTheme.textPrimary, fontWeight: FontWeight.w600, ), ), ], ), centerTitle: true, ), body: SingleChildScrollView( padding: const EdgeInsets.all(Spacing.pagePadding), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Profile Header - Enhanced with better spacing and animations _buildProfileHeader(userData) .animate() .fadeIn(duration: AnimationDuration.pageTransition) .slideY( begin: 0.1, end: 0, curve: AnimationCurves.pageTransition, ), const SizedBox(height: Spacing.sectionGap), // Account Section - Enhanced with improved spacing _buildAccountSection(context, ref) .animate() .fadeIn( delay: AnimationDelay.short, duration: AnimationDuration.pageTransition, ) .slideY( begin: 0.1, end: 0, curve: AnimationCurves.pageTransition, ), ], ), ), ), loading: () => Scaffold( backgroundColor: context.conduitTheme.surfaceBackground, appBar: AppBar( backgroundColor: context.conduitTheme.surfaceBackground, elevation: Elevation.none, automaticallyImplyLeading: false, leading: IconButton( icon: Icon( UiUtils.platformIcon( ios: CupertinoIcons.back, android: Icons.arrow_back, ), color: context.conduitTheme.textPrimary, ), onPressed: () => Navigator.of(context).maybePop(), tooltip: 'Back', ), title: Text( 'You', style: context.conduitTheme.headingSmall?.copyWith( color: context.conduitTheme.textPrimary, fontWeight: FontWeight.w600, ), ), centerTitle: true, ), body: const Center( child: ImprovedLoadingState(message: 'Loading profile...'), ), ), error: (error, stack) => Scaffold( backgroundColor: context.conduitTheme.surfaceBackground, appBar: AppBar( backgroundColor: context.conduitTheme.surfaceBackground, elevation: Elevation.none, automaticallyImplyLeading: false, leading: IconButton( icon: Icon( UiUtils.platformIcon( ios: CupertinoIcons.back, android: Icons.arrow_back, ), color: context.conduitTheme.textPrimary, ), onPressed: () => Navigator.of(context).maybePop(), tooltip: 'Back', ), title: Text( 'You', style: context.conduitTheme.headingSmall?.copyWith( color: context.conduitTheme.textPrimary, fontWeight: FontWeight.w600, ), ), centerTitle: true, ), body: Center( child: ImprovedEmptyState( title: 'Unable to load profile', subtitle: 'Please check your connection and try again', icon: UiUtils.platformIcon( ios: CupertinoIcons.exclamationmark_triangle, android: Icons.error_outline, ), ), ), ), ), ); } Widget _buildProfileHeader(dynamic user) { return Builder( builder: (context) => ConduitCard( padding: const EdgeInsets.all(Spacing.cardPadding), child: Row( children: [ // Enhanced avatar with better sizing and shadows Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(AppBorderRadius.avatar), boxShadow: ConduitShadows.card, ), child: ConduitAvatar( size: IconSize.avatar, text: user?.name?.substring(0, 1) ?? 'U', ), ), const SizedBox(width: Spacing.md), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( user?.name ?? 'User', style: context.conduitTheme.headingMedium?.copyWith( color: context.conduitTheme.textPrimary, fontWeight: FontWeight.w600, ), ), const SizedBox(height: Spacing.xs), Text( user?.email ?? 'No email', style: context.conduitTheme.bodyMedium?.copyWith( color: context.conduitTheme.textSecondary, ), ), const SizedBox(height: Spacing.sm), // Enhanced status badge with better styling Container( padding: const EdgeInsets.symmetric( horizontal: Spacing.sm, vertical: Spacing.xs, ), decoration: BoxDecoration( color: context.conduitTheme.success.withValues( alpha: Alpha.badgeBackground, ), borderRadius: BorderRadius.circular( AppBorderRadius.badge, ), border: Border.all( color: context.conduitTheme.success.withValues( alpha: Alpha.avatarBorder, ), width: BorderWidth.thin, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 6, height: 6, decoration: BoxDecoration( color: context.conduitTheme.success, shape: BoxShape.circle, ), ), const SizedBox(width: Spacing.xs), Text( 'Active', style: context.conduitTheme.label?.copyWith( color: context.conduitTheme.success, fontWeight: FontWeight.w600, ), ), ], ), ), ], ), ), ], ), ), ); } Widget _buildAccountSection(BuildContext context, WidgetRef ref) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Account', style: context.conduitTheme.headingSmall?.copyWith( color: context.conduitTheme.textPrimary, ), ), const SizedBox(height: Spacing.md), ConduitCard( padding: EdgeInsets.zero, child: Column( children: [ _buildDefaultModelTile(context, ref), Divider(color: context.conduitTheme.dividerColor, height: 1), _buildThemeToggleTile(context, ref), Divider(color: context.conduitTheme.dividerColor, height: 1), _buildAboutTile(context), Divider(color: context.conduitTheme.dividerColor, height: 1), _buildAccountOption( icon: UiUtils.platformIcon( ios: CupertinoIcons.square_arrow_left, android: Icons.logout, ), title: 'Sign Out', subtitle: 'End your session', onTap: () => _signOut(context, ref), isDestructive: true, ), ], ), ), ], ); } Widget _buildAccountOption({ required IconData icon, required String title, required String subtitle, required VoidCallback onTap, bool isDestructive = false, }) { return Builder( builder: (context) => ListTile( contentPadding: const EdgeInsets.symmetric( horizontal: Spacing.listItemPadding, vertical: Spacing.sm, ), leading: Container( padding: const EdgeInsets.all(Spacing.sm), decoration: BoxDecoration( color: isDestructive ? context.conduitTheme.error.withValues(alpha: Alpha.highlight) : context.conduitTheme.buttonPrimary.withValues( alpha: Alpha.highlight, ), borderRadius: BorderRadius.circular(AppBorderRadius.small), ), child: Icon( icon, color: isDestructive ? context.conduitTheme.error : context.conduitTheme.buttonPrimary, size: IconSize.medium, ), ), title: Text( title, style: context.conduitTheme.bodyLarge?.copyWith( color: isDestructive ? context.conduitTheme.error : context.conduitTheme.textPrimary, fontWeight: FontWeight.w500, ), ), subtitle: Text( subtitle, style: context.conduitTheme.bodySmall?.copyWith( color: context.conduitTheme.textSecondary, ), ), trailing: Icon( UiUtils.platformIcon( ios: CupertinoIcons.chevron_right, android: Icons.chevron_right, ), color: context.conduitTheme.iconSecondary, size: IconSize.small, ), onTap: onTap, ), ); } Widget _buildDefaultModelTile(BuildContext context, WidgetRef ref) { final settings = ref.watch(appSettingsProvider); final modelsAsync = ref.watch(modelsProvider); return modelsAsync.when( data: (models) { final currentModel = models.firstWhere( (m) => m.id == settings.defaultModel, orElse: () => models.isNotEmpty ? models.first : const Model( id: 'none', name: 'No models available', ), ); return ListTile( contentPadding: const EdgeInsets.symmetric( horizontal: Spacing.listItemPadding, vertical: Spacing.sm, ), leading: Container( padding: const EdgeInsets.all(Spacing.sm), decoration: BoxDecoration( color: context.conduitTheme.buttonPrimary.withValues( alpha: Alpha.highlight, ), borderRadius: BorderRadius.circular(AppBorderRadius.small), ), child: Icon( UiUtils.platformIcon( ios: CupertinoIcons.cube_box, android: Icons.psychology, ), color: context.conduitTheme.buttonPrimary, size: IconSize.medium, ), ), title: Text( 'Default Model', style: context.conduitTheme.bodyLarge?.copyWith( color: context.conduitTheme.textPrimary, fontWeight: FontWeight.w500, ), ), subtitle: Text( settings.defaultModel != null ? currentModel.name : 'Auto-select', style: context.conduitTheme.bodySmall?.copyWith( color: context.conduitTheme.textSecondary, ), ), trailing: Icon( UiUtils.platformIcon( ios: CupertinoIcons.chevron_right, android: Icons.chevron_right, ), color: context.conduitTheme.iconSecondary, size: IconSize.small, ), onTap: () => _showModelSelector(context, ref, models), ); }, loading: () => ListTile( contentPadding: const EdgeInsets.symmetric( horizontal: Spacing.listItemPadding, vertical: Spacing.sm, ), leading: Container( padding: const EdgeInsets.all(Spacing.sm), decoration: BoxDecoration( color: context.conduitTheme.buttonPrimary.withValues( alpha: Alpha.highlight, ), borderRadius: BorderRadius.circular(AppBorderRadius.small), ), child: Icon( UiUtils.platformIcon( ios: CupertinoIcons.cube_box, android: Icons.psychology, ), color: context.conduitTheme.buttonPrimary, size: IconSize.medium, ), ), title: Text( 'Default Model', style: context.conduitTheme.bodyLarge?.copyWith( color: context.conduitTheme.textPrimary, fontWeight: FontWeight.w500, ), ), subtitle: Text( 'Loading models...', style: context.conduitTheme.bodySmall?.copyWith( color: context.conduitTheme.textSecondary, ), ), ), error: (error, stack) => ListTile( contentPadding: const EdgeInsets.symmetric( horizontal: Spacing.listItemPadding, vertical: Spacing.sm, ), leading: Container( padding: const EdgeInsets.all(Spacing.sm), decoration: BoxDecoration( color: context.conduitTheme.error.withValues( alpha: Alpha.highlight, ), borderRadius: BorderRadius.circular(AppBorderRadius.small), ), child: Icon( UiUtils.platformIcon( ios: CupertinoIcons.exclamationmark_triangle, android: Icons.error_outline, ), color: context.conduitTheme.error, size: IconSize.medium, ), ), title: Text( 'Default Model', style: context.conduitTheme.bodyLarge?.copyWith( color: context.conduitTheme.textPrimary, fontWeight: FontWeight.w500, ), ), subtitle: Text( 'Failed to load models', style: context.conduitTheme.bodySmall?.copyWith( color: context.conduitTheme.error, ), ), ), ); } Widget _buildThemeToggleTile(BuildContext context, WidgetRef ref) { final themeMode = ref.watch(themeModeProvider); final platformBrightness = MediaQuery.platformBrightnessOf(context); final bool isDarkEffective = themeMode == ThemeMode.dark || (themeMode == ThemeMode.system && platformBrightness == Brightness.dark); return ListTile( contentPadding: const EdgeInsets.symmetric( horizontal: Spacing.listItemPadding, vertical: Spacing.sm, ), leading: Container( padding: const EdgeInsets.all(Spacing.sm), decoration: BoxDecoration( color: context.conduitTheme.buttonPrimary.withValues( alpha: Alpha.highlight, ), borderRadius: BorderRadius.circular(AppBorderRadius.small), ), child: Icon( UiUtils.platformIcon( ios: CupertinoIcons.moon_stars, android: Icons.dark_mode, ), color: context.conduitTheme.buttonPrimary, size: IconSize.medium, ), ), title: Text( 'Dark Mode', style: context.conduitTheme.bodyLarge?.copyWith( color: context.conduitTheme.textPrimary, fontWeight: FontWeight.w500, ), ), subtitle: Text( themeMode == ThemeMode.system ? 'Following system: ' '${platformBrightness == Brightness.dark ? 'Dark' : 'Light'}' : (isDarkEffective ? 'Currently using Dark theme' : 'Currently using Light theme'), style: context.conduitTheme.bodySmall?.copyWith( color: context.conduitTheme.textSecondary, ), ), trailing: Switch.adaptive( value: isDarkEffective, onChanged: (value) { ref .read(themeModeProvider.notifier) .setTheme(value ? ThemeMode.dark : ThemeMode.light); }, ), onTap: () { final newValue = !isDarkEffective; ref .read(themeModeProvider.notifier) .setTheme(newValue ? ThemeMode.dark : ThemeMode.light); }, ); } Widget _buildAboutTile(BuildContext context) { return _buildAccountOption( icon: UiUtils.platformIcon( ios: CupertinoIcons.info, android: Icons.info_outline, ), title: 'About App', subtitle: 'Conduit information and links', onTap: () => _showAboutDialog(context), ); } Future _showAboutDialog(BuildContext context) async { try { final info = await PackageInfo.fromPlatform(); // Update dialog with dynamic version each time // GitHub repo URL source of truth const githubUrl = 'https://github.com/cogwheel0/conduit'; if (!context.mounted) return; await showDialog( context: context, builder: (ctx) { return AlertDialog( backgroundColor: ctx.conduitTheme.surfaceBackground, title: Text( 'About Conduit', style: ctx.conduitTheme.headingSmall?.copyWith( color: ctx.conduitTheme.textPrimary, ), ), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Version: ${info.version} (${info.buildNumber})', style: ctx.conduitTheme.bodyMedium?.copyWith( color: ctx.conduitTheme.textSecondary, ), ), const SizedBox(height: Spacing.md), InkWell( onTap: () => launchUrlString( githubUrl, mode: LaunchMode.externalApplication, ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( UiUtils.platformIcon( ios: CupertinoIcons.link, android: Icons.link, ), size: IconSize.small, color: ctx.conduitTheme.buttonPrimary, ), const SizedBox(width: Spacing.xs), Text( 'GitHub Repository', style: ctx.conduitTheme.bodyMedium?.copyWith( color: ctx.conduitTheme.buttonPrimary, fontWeight: FontWeight.w600, ), ), ], ), ), ], ), actions: [ TextButton( onPressed: () => Navigator.of(ctx).pop(), child: const Text('Close'), ), ], ); }, ); } catch (e) { if (!context.mounted) return; UiUtils.showMessage(context, 'Unable to load app info'); } } Future _showModelSelector(BuildContext context, WidgetRef ref, List models) async { final result = await showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (ctx) => _DefaultModelBottomSheet( models: models, currentDefaultModelId: ref.read(appSettingsProvider).defaultModel, ), ); // result is non-null only when Save button is pressed // null means the sheet was dismissed without saving if (result != null) { // Handle special case: 'auto-select' should be stored as null final modelIdToSave = result == 'auto-select' ? null : result; await ref.read(appSettingsProvider.notifier).setDefaultModel(modelIdToSave); } } void _signOut(BuildContext context, WidgetRef ref) async { final confirm = await UiUtils.showConfirmationDialog( context, title: 'Sign out?', message: 'You\'ll need to sign in again to continue', confirmText: 'Sign out', isDestructive: true, ); if (confirm) { await ref.read(logoutActionProvider); } } } class _DefaultModelBottomSheet extends ConsumerStatefulWidget { final List models; final String? currentDefaultModelId; const _DefaultModelBottomSheet({ required this.models, required this.currentDefaultModelId, }); @override ConsumerState<_DefaultModelBottomSheet> createState() => _DefaultModelBottomSheetState(); } class _DefaultModelBottomSheetState extends ConsumerState<_DefaultModelBottomSheet> { final TextEditingController _searchController = TextEditingController(); String _searchQuery = ''; List _filteredModels = []; Timer? _searchDebounce; String? _selectedModelId; Widget _capabilityChip({required IconData icon, required String label}) { return Container( margin: const EdgeInsets.only(right: Spacing.xs), padding: const EdgeInsets.symmetric(horizontal: Spacing.xs, vertical: 2), decoration: BoxDecoration( color: context.conduitTheme.buttonPrimary.withValues(alpha: 0.08), borderRadius: BorderRadius.circular(AppBorderRadius.chip), border: Border.all( color: context.conduitTheme.buttonPrimary.withValues(alpha: 0.3), width: BorderWidth.thin, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 12, color: context.conduitTheme.buttonPrimary), const SizedBox(width: 4), Text( label, style: TextStyle( fontSize: AppTypography.labelSmall, color: context.conduitTheme.textSecondary, fontWeight: FontWeight.w500, ), ), ], ), ); } @override void initState() { super.initState(); // If no default model is set (null), default to auto-select _selectedModelId = widget.currentDefaultModelId ?? 'auto-select'; // Add auto-select as first item _filteredModels = [ const Model(id: 'auto-select', name: 'Auto-select'), ...widget.models, ]; } @override void dispose() { _searchController.dispose(); _searchDebounce?.cancel(); super.dispose(); } void _filterModels(String query) { _searchDebounce?.cancel(); _searchDebounce = Timer(const Duration(milliseconds: 160), () { setState(() { _searchQuery = query.toLowerCase(); List allModels = [ const Model(id: 'auto-select', name: 'Auto-select'), ...widget.models, ]; if (_searchQuery.isNotEmpty) { _filteredModels = allModels.where((model) { return model.name.toLowerCase().contains(_searchQuery) || model.id.toLowerCase().contains(_searchQuery); }).toList(); } else { _filteredModels = allModels; } }); }); } @override Widget build(BuildContext context) { return DraggableScrollableSheet( initialChildSize: 0.75, maxChildSize: 0.92, minChildSize: 0.45, builder: (context, scrollController) { return Container( decoration: BoxDecoration( color: context.conduitTheme.surfaceBackground, borderRadius: const BorderRadius.vertical( top: Radius.circular(AppBorderRadius.bottomSheet), ), border: Border.all( color: context.conduitTheme.dividerColor, width: BorderWidth.regular, ), boxShadow: ConduitShadows.modal, ), child: SafeArea( top: false, bottom: true, child: Padding( padding: const EdgeInsets.all(Spacing.bottomSheetPadding), child: Column( children: [ // Handle bar Container( margin: const EdgeInsets.only( top: Spacing.sm, bottom: Spacing.md, ), width: Spacing.xxl, height: Spacing.xs, decoration: BoxDecoration( color: context.conduitTheme.dividerColor, borderRadius: BorderRadius.circular(AppBorderRadius.xs), ), ), // Header Padding( padding: const EdgeInsets.only(bottom: Spacing.md), child: Row( children: [ Expanded( child: Text( 'Default Model', style: context.conduitTheme.headingMedium?.copyWith( color: context.conduitTheme.textPrimary, fontWeight: FontWeight.w600, ), ), ), TextButton( onPressed: () => Navigator.pop(context, _selectedModelId), child: Text( 'Save', style: context.conduitTheme.bodyMedium?.copyWith( color: context.conduitTheme.buttonPrimary, fontWeight: FontWeight.w600, ), ), ), ], ), ), // Search field Padding( padding: const EdgeInsets.only(bottom: Spacing.md), child: TextField( controller: _searchController, style: TextStyle(color: context.conduitTheme.textPrimary), decoration: InputDecoration( hintText: 'Search...', hintStyle: TextStyle( color: context.conduitTheme.inputPlaceholder, ), prefixIcon: Icon( Platform.isIOS ? CupertinoIcons.search : Icons.search, color: context.conduitTheme.iconSecondary, ), filled: true, fillColor: context.conduitTheme.inputBackground, border: OutlineInputBorder( borderRadius: BorderRadius.circular(AppBorderRadius.md), borderSide: BorderSide.none, ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(AppBorderRadius.md), borderSide: BorderSide( color: context.conduitTheme.inputBorder, width: 1, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(AppBorderRadius.md), borderSide: BorderSide( color: context.conduitTheme.buttonPrimary, width: 1, ), ), contentPadding: const EdgeInsets.symmetric( horizontal: Spacing.md, vertical: Spacing.md, ), ), onChanged: _filterModels, ), ), const SizedBox(height: Spacing.sm), // Models list Expanded( child: Scrollbar( controller: scrollController, child: _filteredModels.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Platform.isIOS ? CupertinoIcons.search_circle : Icons.search_off, size: 48, color: context.conduitTheme.iconSecondary, ), const SizedBox(height: Spacing.md), Text( 'No results', style: TextStyle( color: context.conduitTheme.textSecondary, fontSize: AppTypography.bodyLarge, ), ), ], ), ) : ListView.builder( controller: scrollController, padding: EdgeInsets.zero, itemCount: _filteredModels.length, itemBuilder: (context, index) { final model = _filteredModels[index]; final isAutoSelect = model.id == 'auto-select'; final isSelected = isAutoSelect ? _selectedModelId == null || _selectedModelId == 'auto-select' : _selectedModelId == model.id; return _buildModelListTile( model: model, isSelected: isSelected, isAutoSelect: isAutoSelect, onTap: () { setState(() { _selectedModelId = isAutoSelect ? 'auto-select' : model.id; }); }, ); }, ), ), ), ], ), ), ), ); }, ); } bool _modelSupportsReasoning(Model model) { final params = model.supportedParameters ?? const []; return params.any((p) => p.toLowerCase().contains('reasoning')); } Widget _buildModelListTile({ required Model model, required bool isSelected, required bool isAutoSelect, required VoidCallback onTap, }) { return PressableScale( onTap: onTap, borderRadius: BorderRadius.circular(AppBorderRadius.md), child: Container( margin: const EdgeInsets.only(bottom: Spacing.md), decoration: BoxDecoration( gradient: isSelected ? LinearGradient( colors: [ context.conduitTheme.buttonPrimary.withValues(alpha: 0.2), context.conduitTheme.buttonPrimary.withValues(alpha: 0.1), ], ) : null, color: isSelected ? null : context.conduitTheme.surfaceBackground.withValues(alpha: 0.05), borderRadius: BorderRadius.circular(AppBorderRadius.md), border: Border.all( color: isSelected ? context.conduitTheme.buttonPrimary.withValues(alpha: 0.5) : context.conduitTheme.dividerColor, width: BorderWidth.regular, ), boxShadow: isSelected ? ConduitShadows.card : null, ), child: Padding( padding: const EdgeInsets.symmetric( horizontal: Spacing.md, vertical: Spacing.sm, ), child: Row( children: [ Container( width: 32, height: 32, decoration: BoxDecoration( color: context.conduitTheme.buttonPrimary.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(AppBorderRadius.md), ), child: Icon( isAutoSelect ? (Platform.isIOS ? CupertinoIcons.wand_stars : Icons.auto_awesome) : (Platform.isIOS ? CupertinoIcons.cube : Icons.psychology), color: context.conduitTheme.buttonPrimary, size: 16, ), ), const SizedBox(width: Spacing.md), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( isAutoSelect ? 'Auto-select' : model.name, style: TextStyle( color: context.conduitTheme.textPrimary, fontWeight: FontWeight.w600, fontSize: AppTypography.bodyMedium, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), if (isAutoSelect) ...[ const SizedBox(height: Spacing.xs), Text( 'Let the app choose the best model', style: TextStyle( fontSize: AppTypography.bodySmall, color: context.conduitTheme.textSecondary, fontWeight: FontWeight.w400, ), ), ] else ...[ const SizedBox(height: Spacing.xs), Row( children: [ if (model.isMultimodal) _capabilityChip( icon: Platform.isIOS ? CupertinoIcons.photo : Icons.image, label: 'Multimodal', ), if (_modelSupportsReasoning(model)) _capabilityChip( icon: Platform.isIOS ? CupertinoIcons.lightbulb : Icons.psychology_alt, label: 'Reasoning', ), ], ), ], ], ), ), const SizedBox(width: Spacing.md), AnimatedOpacity( opacity: isSelected ? 1 : 0.6, duration: AnimationDuration.fast, child: Container( padding: const EdgeInsets.all(Spacing.xxs), decoration: BoxDecoration( color: isSelected ? context.conduitTheme.buttonPrimary : context.conduitTheme.surfaceBackground, borderRadius: BorderRadius.circular(AppBorderRadius.md), border: Border.all( color: isSelected ? context.conduitTheme.buttonPrimary.withValues(alpha: 0.6) : context.conduitTheme.dividerColor, ), ), child: Icon( isSelected ? (Platform.isIOS ? CupertinoIcons.check_mark : Icons.check) : (Platform.isIOS ? CupertinoIcons.add : Icons.add), color: isSelected ? context.conduitTheme.textInverse : context.conduitTheme.iconSecondary, size: 14, ), ), ), ], ), ), ), ).animate().fadeIn(duration: AnimationDuration.microInteraction); } }