diff --git a/lib/core/error/enhanced_error_service.dart b/lib/core/error/enhanced_error_service.dart index 38bde49..3d9a8ec 100644 --- a/lib/core/error/enhanced_error_service.dart +++ b/lib/core/error/enhanced_error_service.dart @@ -99,6 +99,8 @@ class EnhancedErrorService { final message = getUserMessage(error); final isRetryableError = isRetryable(error); final retryDelay = getRetryDelay(error); + final theme = context.conduitTheme; + final inverseColor = theme.textInverse; final snackBar = SnackBar( content: Column( @@ -109,15 +111,12 @@ class EnhancedErrorService { children: [ Icon( _getErrorIcon(error), - color: AppTheme.neutral50, + color: inverseColor, size: IconSize.md, ), const SizedBox(width: Spacing.sm), Expanded( - child: Text( - message, - style: const TextStyle(color: AppTheme.neutral50), - ), + child: Text(message, style: TextStyle(color: inverseColor)), ), ], ), @@ -126,7 +125,7 @@ class EnhancedErrorService { Text( getTechnicalDetails(error), style: TextStyle( - color: AppTheme.neutral50.withValues(alpha: Alpha.strong), + color: inverseColor.withValues(alpha: Alpha.strong), fontSize: AppTypography.labelMedium, ), ), @@ -140,7 +139,7 @@ class EnhancedErrorService { label: retryDelay != null && retryDelay.inSeconds > 5 ? "${AppLocalizations.of(context)!.retry} (${retryDelay.inSeconds}s)" : AppLocalizations.of(context)!.retry, - textColor: AppTheme.neutral50, + textColor: inverseColor, onPressed: onRetry, ) : null, @@ -166,6 +165,7 @@ class EnhancedErrorService { context: context, barrierDismissible: true, builder: (BuildContext context) { + final theme = context.conduitTheme; return AlertDialog( title: Row( children: [ @@ -178,25 +178,29 @@ class EnhancedErrorService { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(message), + Text(message, style: TextStyle(color: theme.textPrimary)), if (showTechnicalDetails) ...[ const SizedBox(height: Spacing.md), - const Text( + Text( 'Technical Details:', - style: TextStyle(fontWeight: FontWeight.bold), + style: TextStyle( + fontWeight: FontWeight.bold, + color: theme.textPrimary, + ), ), const SizedBox(height: Spacing.xs), Container( padding: const EdgeInsets.all(Spacing.sm), decoration: BoxDecoration( - color: AppTheme.neutral100, + color: theme.surfaceContainer, borderRadius: BorderRadius.circular(AppBorderRadius.xs), ), child: Text( technicalDetails, - style: const TextStyle( + style: TextStyle( fontFamily: AppTypography.monospaceFontFamily, fontSize: AppTypography.labelMedium, + color: theme.textSecondary, ), ), ), @@ -236,6 +240,7 @@ class EnhancedErrorService { final message = getUserMessage(error); final technicalDetails = getTechnicalDetails(error); final isRetryableError = isRetryable(error); + final theme = context.conduitTheme; return Container( padding: padding ?? const EdgeInsets.all(Spacing.md), @@ -260,21 +265,22 @@ class EnhancedErrorService { Text( message, textAlign: TextAlign.center, - style: TextStyle(color: AppTheme.neutral600), + style: TextStyle(color: theme.textSecondary), ), if (showTechnicalDetails) ...[ const SizedBox(height: Spacing.md), Container( padding: const EdgeInsets.all(Spacing.xs), decoration: BoxDecoration( - color: AppTheme.neutral100, + color: theme.surfaceContainer, borderRadius: BorderRadius.circular(AppBorderRadius.sm), ), child: Text( technicalDetails, - style: const TextStyle( + style: TextStyle( fontFamily: AppTypography.monospaceFontFamily, fontSize: AppTypography.labelMedium, + color: theme.textSecondary, ), ), ), diff --git a/lib/features/auth/views/authentication_page.dart b/lib/features/auth/views/authentication_page.dart index 0b85cc3..779177c 100644 --- a/lib/features/auth/views/authentication_page.dart +++ b/lib/features/auth/views/authentication_page.dart @@ -314,6 +314,7 @@ class _AuthenticationPageState extends ConsumerState { size: 48, useGradient: true, addShadow: true, + context: context, ).animate().scale( duration: AnimationDuration.pageTransition, curve: Curves.easeOutBack, diff --git a/lib/features/auth/views/server_connection_page.dart b/lib/features/auth/views/server_connection_page.dart index 2206429..cceeb29 100644 --- a/lib/features/auth/views/server_connection_page.dart +++ b/lib/features/auth/views/server_connection_page.dart @@ -356,6 +356,7 @@ class _ServerConnectionPageState extends ConsumerState { size: 64, useGradient: true, addShadow: true, + context: context, ), // Reviewer mode badge if (reviewerMode) diff --git a/lib/shared/theme/theme_extensions.dart b/lib/shared/theme/theme_extensions.dart index 0562f75..5582bcf 100644 --- a/lib/shared/theme/theme_extensions.dart +++ b/lib/shared/theme/theme_extensions.dart @@ -1,3 +1,5 @@ +import 'dart:math' as math; + import 'package:flutter/material.dart'; // Using system fonts; no GoogleFonts dependency required import 'app_theme.dart'; @@ -505,10 +507,11 @@ class ConduitThemeExtension extends ThemeExtension { /// Dark theme extension derived from the active color palette. static ConduitThemeExtension darkPalette(AppColorPalette palette) { final darkTone = palette.dark; + final onDarkPrimary = _onSurfaceColor(darkTone.primary); return ConduitThemeExtension( chatBubbleUser: darkTone.primary, chatBubbleAssistant: const Color(0xFF0E1010), - chatBubbleUserText: AppTheme.neutral50, + chatBubbleUserText: onDarkPrimary, chatBubbleAssistantText: AppTheme.neutral50, chatBubbleUserBorder: darkTone.secondary, chatBubbleAssistantBorder: const Color(0xFF1A1D1C), @@ -525,7 +528,7 @@ class ConduitThemeExtension extends ThemeExtension { surfaceContainer: const Color(0xFF0C0F0E), surfaceContainerHighest: const Color(0xFF121514), buttonPrimary: darkTone.primary, - buttonPrimaryText: AppTheme.neutral50, + buttonPrimaryText: onDarkPrimary, buttonSecondary: const Color(0xFF151918), buttonSecondaryText: AppTheme.neutral50, buttonDisabled: AppTheme.neutral600, @@ -622,10 +625,11 @@ class ConduitThemeExtension extends ThemeExtension { static ConduitThemeExtension lightPalette(AppColorPalette palette) { final lightTone = palette.light; final darkTone = palette.dark; + final onLightPrimary = _onSurfaceColor(lightTone.primary); return ConduitThemeExtension( chatBubbleUser: lightTone.primary, chatBubbleAssistant: const Color(0xFFF7F7F7), - chatBubbleUserText: AppTheme.neutral50, + chatBubbleUserText: onLightPrimary, chatBubbleAssistantText: const Color(0xFF1C1C1C), chatBubbleUserBorder: darkTone.primary, chatBubbleAssistantBorder: const Color(0xFFE7E7E7), @@ -642,7 +646,7 @@ class ConduitThemeExtension extends ThemeExtension { surfaceContainer: const Color(0xFFF7F7F7), surfaceContainerHighest: const Color(0xFFF0F1F1), buttonPrimary: lightTone.primary, - buttonPrimaryText: AppTheme.neutral50, + buttonPrimaryText: onLightPrimary, buttonSecondary: const Color(0xFFF0F1F1), buttonSecondaryText: const Color(0xFF1C1C1C), buttonDisabled: AppTheme.neutral300, @@ -738,6 +742,22 @@ class ConduitThemeExtension extends ThemeExtension { static Color _surfaceTint(Color tone, Color surface, double opacity) { return Color.alphaBlend(tone.withValues(alpha: opacity), surface); } + + static Color _onSurfaceColor(Color background) { + final contrastOnLight = _contrastRatio(background, AppTheme.neutral50); + final contrastOnDark = _contrastRatio(background, AppTheme.neutral900); + return contrastOnLight >= contrastOnDark + ? AppTheme.neutral50 + : AppTheme.neutral900; + } + + static double _contrastRatio(Color a, Color b) { + final luminanceA = a.computeLuminance(); + final luminanceB = b.computeLuminance(); + final lighter = math.max(luminanceA, luminanceB); + final darker = math.min(luminanceA, luminanceB); + return (lighter + 0.05) / (darker + 0.05); + } } /// Extension method to easily access Conduit theme from BuildContext diff --git a/lib/shared/widgets/conduit_components.dart b/lib/shared/widgets/conduit_components.dart index df9b2c4..02cab0d 100644 --- a/lib/shared/widgets/conduit_components.dart +++ b/lib/shared/widgets/conduit_components.dart @@ -557,6 +557,7 @@ class ConduitAvatar extends StatelessWidget { return BrandService.createBrandAvatar( size: isCompact ? size * 0.8 : size, fallbackText: text, + context: context, ); } }