diff --git a/.env.example b/.env.example index 1b6a568..8144ca4 100644 --- a/.env.example +++ b/.env.example @@ -79,5 +79,5 @@ ERYAO_CORS__ALLOW_ORIGINS=["http://localhost", "http://localhost:3000"] ############ # Test相关 ############ -ERYAO_TEST__EMAIL=8613812345678 -ERYAO_TEST__PASSWORD=Test@123456 +ERYAO_TEST__EMAIL=test@example.com +ERYAO_TEST__CODE=123456 diff --git a/AGENTS.md b/AGENTS.md index 47ad053..5f0c1fa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -42,6 +42,10 @@ Do not place backend/frontend implementation details here. When viewing data in the database, use `supabase mcp` tools (`supabase_execute_sql`, `supabase_list_tables`, etc.) instead of direct queries or other methods. +## Image Handling + +When reading images, use `understand_image` tool instead of `Read` tool, especially when the model supports multimodal capabilities. Only use `Read` tool for non-image files. + ## Mobile Automation Use Midscene Skills for mobile UI automation. diff --git a/QQ20260406-003407.png b/QQ20260406-003407.png deleted file mode 100644 index 0fd1494..0000000 Binary files a/QQ20260406-003407.png and /dev/null differ diff --git a/apps/lib/features/auth/presentation/screens/login_screen.dart b/apps/lib/features/auth/presentation/screens/login_screen.dart index b7789d5..629ad8b 100644 --- a/apps/lib/features/auth/presentation/screens/login_screen.dart +++ b/apps/lib/features/auth/presentation/screens/login_screen.dart @@ -12,6 +12,7 @@ import '../../../settings/presentation/utils/legal_document_assets.dart'; import '../../../../l10n/app_localizations.dart'; import '../../../../shared/theme/design_tokens.dart'; import '../../../../shared/widgets/app_modal_dialog.dart'; +import '../../../../shared/widgets/gua_icon.dart'; import '../../../../shared/widgets/toast/toast.dart'; import '../../../../shared/widgets/toast/toast_type.dart'; @@ -193,7 +194,7 @@ class _LoginScreenState extends State { return AppModalDialog( title: title, message: content, - icon: Icons.description_outlined, + iconWidget: const GuaIcon(), actions: [ AppModalDialogAction( label: AppLocalizations.of(dialogContext)!.dialogConfirm, diff --git a/apps/lib/features/divination/presentation/screens/auto_divination_screen.dart b/apps/lib/features/divination/presentation/screens/auto_divination_screen.dart index 171a1b2..86df843 100644 --- a/apps/lib/features/divination/presentation/screens/auto_divination_screen.dart +++ b/apps/lib/features/divination/presentation/screens/auto_divination_screen.dart @@ -10,6 +10,7 @@ import 'package:vibration/vibration.dart'; import '../../../../l10n/app_localizations.dart'; import '../../../../shared/theme/design_tokens.dart'; import '../../../../shared/widgets/app_modal_dialog.dart'; +import '../../../../shared/widgets/gua_icon.dart'; import '../../../../shared/widgets/divination/divination_shared_widgets.dart'; import '../../../../shared/widgets/divination/divination_terms.dart'; import '../../../../shared/widgets/divination/yao_legend.dart'; @@ -253,7 +254,7 @@ class _AutoDivinationScreenState extends State points.runCost, points.availableBalance, ), - icon: Icons.auto_awesome_rounded, + iconWidget: const GuaIcon(), actions: [ AppModalDialogAction( label: l10n.cancel, @@ -290,12 +291,11 @@ class _AutoDivinationScreenState extends State ), ); } finally { - if (!mounted) { - return; + if (mounted) { + setState(() { + _submitting = false; + }); } - setState(() { - _submitting = false; - }); } } diff --git a/apps/lib/features/divination/presentation/screens/divination_screen.dart b/apps/lib/features/divination/presentation/screens/divination_screen.dart index dad6bd3..7c8aa3e 100644 --- a/apps/lib/features/divination/presentation/screens/divination_screen.dart +++ b/apps/lib/features/divination/presentation/screens/divination_screen.dart @@ -6,6 +6,7 @@ import '../../../../data/network/api_client.dart'; import '../../../../l10n/app_localizations.dart'; import '../../../../shared/theme/design_tokens.dart'; import '../../../../shared/widgets/app_modal_dialog.dart'; +import '../../../../shared/widgets/gua_icon.dart'; import '../../../../shared/widgets/divination/divination_shared_widgets.dart'; import '../../../../shared/widgets/toast/toast.dart'; import '../../../../shared/widgets/toast/toast_type.dart'; @@ -385,7 +386,7 @@ Future _showMethodTip(BuildContext context, AppLocalizations l10n) { title: l10n.divinationMethodTipTitle, message: '${l10n.divinationMethodTipAuto}\n\n${l10n.divinationMethodTipManual}\n\n${l10n.divinationMethodTipRecommend}', - icon: Icons.lightbulb_outline_rounded, + iconWidget: const GuaIcon(), actions: [ AppModalDialogAction( label: l10n.divinationIAcknowledge, diff --git a/apps/lib/features/divination/presentation/screens/manual_divination_screen.dart b/apps/lib/features/divination/presentation/screens/manual_divination_screen.dart index a452c21..f14bbe0 100644 --- a/apps/lib/features/divination/presentation/screens/manual_divination_screen.dart +++ b/apps/lib/features/divination/presentation/screens/manual_divination_screen.dart @@ -5,6 +5,7 @@ import 'package:intl/intl.dart'; import '../../../../l10n/app_localizations.dart'; import '../../../../shared/theme/design_tokens.dart'; import '../../../../shared/widgets/app_modal_dialog.dart'; +import '../../../../shared/widgets/gua_icon.dart'; import '../../../../shared/widgets/divination/divination_shared_widgets.dart'; import '../../../../shared/widgets/divination/divination_terms.dart'; import '../../../../shared/widgets/divination/yao_legend.dart'; @@ -166,7 +167,7 @@ class _ManualDivinationScreenState extends State return AppModalDialog( title: l10n.manualYaoTipTitle, message: l10n.manualYaoTipContent, - icon: Icons.info_outline_rounded, + iconWidget: const GuaIcon(), actions: [ AppModalDialogAction( label: l10n.divinationIAcknowledge, @@ -210,7 +211,7 @@ class _ManualDivinationScreenState extends State points.runCost, points.availableBalance, ), - icon: Icons.auto_awesome_rounded, + iconWidget: const GuaIcon(), actions: [ AppModalDialogAction( label: l10n.cancel, @@ -247,12 +248,11 @@ class _ManualDivinationScreenState extends State ), ); } finally { - if (!mounted) { - return; + if (mounted) { + setState(() { + _submitting = false; + }); } - setState(() { - _submitting = false; - }); } } } @@ -506,18 +506,19 @@ class _YaoSelectionCard extends StatelessWidget { width: double.infinity, height: 120, fit: BoxFit.contain, - errorBuilder: (_, __, ___) => Container( - height: 120, - color: colors.errorContainer, - child: Center( - child: Text( - l10n.divinationClose, - style: TextStyle( - color: colors.onErrorContainer, + errorBuilder: (context, error, stackTrace) => + Container( + height: 120, + color: colors.errorContainer, + child: Center( + child: Text( + l10n.divinationClose, + style: TextStyle( + color: colors.onErrorContainer, + ), + ), ), ), - ), - ), ), ), const SizedBox(height: AppSpacing.xs), diff --git a/apps/lib/features/settings/presentation/screens/settings_screen.dart b/apps/lib/features/settings/presentation/screens/settings_screen.dart index 7ed064c..57516ca 100644 --- a/apps/lib/features/settings/presentation/screens/settings_screen.dart +++ b/apps/lib/features/settings/presentation/screens/settings_screen.dart @@ -4,6 +4,7 @@ import '../../../../l10n/app_localizations.dart'; import '../../../../core/logging/logger.dart'; import '../../../../shared/theme/design_tokens.dart'; import '../../../../shared/widgets/app_modal_dialog.dart'; +import '../../../../shared/widgets/gua_icon.dart'; import '../../../../shared/widgets/toast/toast.dart'; import '../../../../shared/widgets/toast/toast_type.dart'; import '../../data/models/profile_settings.dart'; @@ -40,7 +41,6 @@ class SettingsScreen extends StatefulWidget { class _SettingsScreenState extends State { final Logger _logger = getLogger('features.settings.settings_screen'); late ProfileSettingsV1 _settings; - bool _isLoggingOut = false; @override void initState() { @@ -114,7 +114,7 @@ class _SettingsScreenState extends State { ), const SizedBox(height: AppSpacing.xl), FilledButton( - onPressed: _isLoggingOut ? null : _confirmLogout, + onPressed: _confirmLogout, style: FilledButton.styleFrom( elevation: 0, backgroundColor: colors.error, @@ -208,7 +208,7 @@ class _SettingsScreenState extends State { return AppModalDialog( title: l10n.settingsLogoutDialogTitle, message: l10n.settingsLogoutDialogBody, - icon: Icons.logout_rounded, + iconWidget: const GuaIcon(), actions: [ AppModalDialogAction( label: l10n.settingsCancel, @@ -228,21 +228,10 @@ class _SettingsScreenState extends State { return; } - setState(() { - _isLoggingOut = true; - }); - try { - await widget.onLogout(); - if (!mounted) { - return; - } - Navigator.of(context).popUntil((route) => route.isFirst); - } finally { - if (mounted) { - setState(() { - _isLoggingOut = false; - }); - } + await widget.onLogout(); + if (!mounted) { + return; } + Navigator.of(context).popUntil((route) => route.isFirst); } } diff --git a/apps/lib/shared/widgets/app_modal_dialog.dart b/apps/lib/shared/widgets/app_modal_dialog.dart index fe6c9b5..c9e2dc4 100644 --- a/apps/lib/shared/widgets/app_modal_dialog.dart +++ b/apps/lib/shared/widgets/app_modal_dialog.dart @@ -23,11 +23,13 @@ class AppModalDialog extends StatelessWidget { required this.message, required this.actions, this.icon, + this.iconWidget, }); final String title; final String message; final IconData? icon; + final Widget? iconWidget; final List actions; @override @@ -59,9 +61,9 @@ class AppModalDialog extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - if (icon != null) + if (iconWidget != null || icon != null) Container( width: 36, height: 36, @@ -70,9 +72,12 @@ class AppModalDialog extends StatelessWidget { borderRadius: BorderRadius.circular(AppRadius.md), ), alignment: Alignment.center, - child: Icon(icon, color: colors.primary, size: 20), + child: + iconWidget ?? + Icon(icon, color: colors.primary, size: 20), ), - if (icon != null) const SizedBox(width: AppSpacing.sm), + if (iconWidget != null || icon != null) + const SizedBox(width: AppSpacing.sm), Expanded( child: Text( title, diff --git a/apps/lib/shared/widgets/gua_icon.dart b/apps/lib/shared/widgets/gua_icon.dart new file mode 100644 index 0000000..79264f9 --- /dev/null +++ b/apps/lib/shared/widgets/gua_icon.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +class GuaIcon extends StatelessWidget { + const GuaIcon({super.key, this.color, this.size = 20}); + + final Color? color; + final double size; + + @override + Widget build(BuildContext context) { + final effectiveColor = + color ?? Theme.of(context).colorScheme.onPrimaryContainer; + return Text( + '爻', + style: TextStyle( + fontSize: size * 0.85, + fontWeight: FontWeight.w700, + color: effectiveColor, + ), + ); + } +}