diff --git a/lib/app/router/app_router.g.dart b/lib/app/router/app_router.g.dart index 4a32207..1d002d2 100644 --- a/lib/app/router/app_router.g.dart +++ b/lib/app/router/app_router.g.dart @@ -6,7 +6,6 @@ part of 'app_router.dart'; // RiverpodGenerator // ************************************************************************** - /// See also [appRouter]. @ProviderFor(appRouter) final appRouterProvider = Provider.internal( diff --git a/lib/features/auth/presentation/providers/auth_provider.g.dart b/lib/features/auth/presentation/providers/auth_provider.g.dart index 05dddb0..d9a8a31 100644 --- a/lib/features/auth/presentation/providers/auth_provider.g.dart +++ b/lib/features/auth/presentation/providers/auth_provider.g.dart @@ -6,7 +6,6 @@ part of 'auth_provider.dart'; // RiverpodGenerator // ************************************************************************** - /// See also [supabaseClient]. @ProviderFor(supabaseClient) final supabaseClientProvider = Provider.internal( diff --git a/lib/features/home/domain/entities/translation_result.dart b/lib/features/home/domain/entities/translation_result.dart index 08f6047..b11d410 100644 --- a/lib/features/home/domain/entities/translation_result.dart +++ b/lib/features/home/domain/entities/translation_result.dart @@ -22,7 +22,11 @@ class TranslationResult { final bool isBookmarked; final DateTime timestamp; - TranslationResult copyWith({int? id, bool? isBookmarked, String? aiResponse}) { + TranslationResult copyWith({ + int? id, + bool? isBookmarked, + String? aiResponse, + }) { return TranslationResult( id: id ?? this.id, inputText: inputText, diff --git a/lib/features/home/presentation/providers/streak_provider.dart b/lib/features/home/presentation/providers/streak_provider.dart index 05868e1..adad05c 100644 --- a/lib/features/home/presentation/providers/streak_provider.dart +++ b/lib/features/home/presentation/providers/streak_provider.dart @@ -33,13 +33,10 @@ Future streak(Ref ref) async { } int _computeStreak(List> rows) { - final Set completionDates = rows - .map((Map r) { - final DateTime d = - DateTime.parse(r['completed_at'] as String).toLocal(); - return _dateKey(d); - }) - .toSet(); + final Set completionDates = rows.map((Map r) { + final DateTime d = DateTime.parse(r['completed_at'] as String).toLocal(); + return _dateKey(d); + }).toSet(); if (completionDates.isEmpty) return 0; diff --git a/lib/features/home/presentation/providers/translate_sketchpad_controller.dart b/lib/features/home/presentation/providers/translate_sketchpad_controller.dart index d45fc06..6d115b6 100644 --- a/lib/features/home/presentation/providers/translate_sketchpad_controller.dart +++ b/lib/features/home/presentation/providers/translate_sketchpad_controller.dart @@ -123,8 +123,9 @@ class TranslateSketchpadController extends Notifier { .analyzeImage(imageBytes, prompt: prompt)) { buffer.write(chunk); final String cleaned = cleanAssistantOutput(buffer.toString()); - final String displayResponse = - GemmaPrompts.parseThinkBlock(cleaned).answer; + final String displayResponse = GemmaPrompts.parseThinkBlock( + cleaned, + ).answer; state = state.copyWith( aiBusy: true, aiResponse: displayResponse, @@ -132,8 +133,9 @@ class TranslateSketchpadController extends Notifier { ); } final String cleaned = cleanAssistantOutput(buffer.toString()); - final String displayResponse = - GemmaPrompts.parseThinkBlock(cleaned).answer; + final String displayResponse = GemmaPrompts.parseThinkBlock( + cleaned, + ).answer; state = state.copyWith( aiBusy: false, aiResponse: displayResponse, @@ -166,8 +168,9 @@ class TranslateSketchpadController extends Notifier { await for (final String chunk in stream) { buffer.write(chunk); final String cleaned = cleanAssistantOutput(buffer.toString()); - final String displayResponse = - GemmaPrompts.parseThinkBlock(cleaned).answer; + final String displayResponse = GemmaPrompts.parseThinkBlock( + cleaned, + ).answer; state = state.copyWith( aiBusy: true, aiResponse: displayResponse, @@ -175,8 +178,9 @@ class TranslateSketchpadController extends Notifier { ); } final String cleaned = cleanAssistantOutput(buffer.toString()); - final String displayResponse = - GemmaPrompts.parseThinkBlock(cleaned).answer; + final String displayResponse = GemmaPrompts.parseThinkBlock( + cleaned, + ).answer; state = state.copyWith( aiBusy: false, aiResponse: displayResponse, diff --git a/lib/features/home/presentation/providers/translation_history_provider.dart b/lib/features/home/presentation/providers/translation_history_provider.dart index 7eedf3f..4490b88 100644 --- a/lib/features/home/presentation/providers/translation_history_provider.dart +++ b/lib/features/home/presentation/providers/translation_history_provider.dart @@ -64,9 +64,10 @@ class TranslationHistoryNotifier saved = await _ds.insert(result); final List current = state.value ?? []; - state = AsyncData>( - [saved, ...current], - ); + state = AsyncData>([ + saved, + ...current, + ]); } catch (_) { // Local save failure is non-fatal } @@ -104,9 +105,10 @@ class TranslationHistoryNotifier await _ds.updateAiResponse(latest.id!, text); } catch (_) {} } - state = AsyncData>( - [updated, ...current.skip(1)], - ); + state = AsyncData>([ + updated, + ...current.skip(1), + ]); } Future clearHistory() async { @@ -156,17 +158,19 @@ class TranslationHistoryNotifier .order('created_at', ascending: false) .limit(100); - return rows.map((dynamic row) { - final Map r = row as Map; - return TranslationResult( - inputText: r['input_text'] as String? ?? '', - baybayinText: r['output_baybayin'] as String? ?? '', - latinText: r['output_latin'] as String? ?? '', - direction: r['direction'] as String? ?? 'latin_to_baybayin', - aiResponse: r['ai_response'] as String? ?? '', - isBookmarked: r['is_bookmarked'] as bool? ?? false, - timestamp: DateTime.parse(r['created_at'] as String), - ); - }).toList(growable: false); + return rows + .map((dynamic row) { + final Map r = row as Map; + return TranslationResult( + inputText: r['input_text'] as String? ?? '', + baybayinText: r['output_baybayin'] as String? ?? '', + latinText: r['output_latin'] as String? ?? '', + direction: r['direction'] as String? ?? 'latin_to_baybayin', + aiResponse: r['ai_response'] as String? ?? '', + isBookmarked: r['is_bookmarked'] as bool? ?? false, + timestamp: DateTime.parse(r['created_at'] as String), + ); + }) + .toList(growable: false); } } diff --git a/lib/features/home/presentation/screens/butty_data_screen.dart b/lib/features/home/presentation/screens/butty_data_screen.dart index 5781e1f..8459726 100644 --- a/lib/features/home/presentation/screens/butty_data_screen.dart +++ b/lib/features/home/presentation/screens/butty_data_screen.dart @@ -106,19 +106,20 @@ class _HeroBanner extends StatelessWidget { Positioned( right: -10, bottom: -8, - child: Image.asset( - 'assets/brand/ButtyTextBubble.webp', - height: 150, - fit: BoxFit.fitHeight, - ) - .animate(delay: 100.ms) - .slideX( - begin: 0.25, - end: 0, - duration: 420.ms, - curve: Curves.easeOutCubic, - ) - .fadeIn(duration: 320.ms), + child: + Image.asset( + 'assets/brand/ButtyTextBubble.webp', + height: 150, + fit: BoxFit.fitHeight, + ) + .animate(delay: 100.ms) + .slideX( + begin: 0.25, + end: 0, + duration: 420.ms, + curve: Curves.easeOutCubic, + ) + .fadeIn(duration: 320.ms), ), Padding( padding: const EdgeInsets.fromLTRB(20, 56, 150, 18), @@ -147,18 +148,17 @@ class _HeroBanner extends StatelessWidget { ), const SizedBox(height: 8), Text( - 'Tara, ayusin natin!\nYour chats and what I remember about you.', - style: TextStyle( - fontSize: 14.5, - fontWeight: FontWeight.w700, - color: cs.onPrimary, - height: 1.4, - ), - ).animate(delay: 80.ms).fadeIn(duration: 300.ms).slideY( - begin: 0.1, - end: 0, - duration: 300.ms, - ), + 'Tara, ayusin natin!\nYour chats and what I remember about you.', + style: TextStyle( + fontSize: 14.5, + fontWeight: FontWeight.w700, + color: cs.onPrimary, + height: 1.4, + ), + ) + .animate(delay: 80.ms) + .fadeIn(duration: 300.ms) + .slideY(begin: 0.1, end: 0, duration: 300.ms), ], ), ), @@ -575,10 +575,7 @@ MarkdownStyleSheet _markdownStyle(BuildContext context, TextStyle base) { strong: base.copyWith(fontWeight: FontWeight.w700), em: base.copyWith(fontStyle: FontStyle.italic), listBullet: base, - a: base.copyWith( - color: cs.primary, - decoration: TextDecoration.underline, - ), + a: base.copyWith(color: cs.primary, decoration: TextDecoration.underline), code: base.copyWith( fontFamily: 'monospace', fontSize: 13, @@ -638,14 +635,16 @@ class _MemorySection extends ConsumerWidget { ); if (draft == null) return; final DateTime now = DateTime.now(); - await ref.read(chatMemoryNotifierProvider.notifier).addFacts([ - ChatMemoryFact( - factType: draft.factType, - content: draft.content, - createdAt: now, - lastReferencedAt: now, - ), - ]); + await ref + .read(chatMemoryNotifierProvider.notifier) + .addFacts([ + ChatMemoryFact( + factType: draft.factType, + content: draft.content, + createdAt: now, + lastReferencedAt: now, + ), + ]); } } @@ -711,15 +710,14 @@ class _MemoryBody extends ConsumerWidget { } final _MemoryFactDraft? draft = result.draft; if (draft == null) return; - await ref.read(chatMemoryNotifierProvider.notifier).updateFact( + await ref + .read(chatMemoryNotifierProvider.notifier) + .updateFact( f.copyWith(factType: draft.factType, content: draft.content), ); } - Future _confirmClearMemory( - BuildContext context, - WidgetRef ref, - ) async { + Future _confirmClearMemory(BuildContext context, WidgetRef ref) async { final bool? confirmed = await showDialog( context: context, builder: (BuildContext ctx) { @@ -833,9 +831,7 @@ class _MemoryFactDraft { class _EditFactResult { const _EditFactResult.update(this.draft) : delete = false; - const _EditFactResult.delete() - : draft = null, - delete = true; + const _EditFactResult.delete() : draft = null, delete = true; final _MemoryFactDraft? draft; final bool delete; } @@ -886,10 +882,8 @@ class _EditFactDialogState extends State<_EditFactDialog> { ), items: _memoryTypes .map( - (String t) => DropdownMenuItem( - value: t, - child: Text(t), - ), + (String t) => + DropdownMenuItem(value: t, child: Text(t)), ) .toList(growable: false), onChanged: (String? v) { @@ -993,9 +987,7 @@ class _SectionCard extends StatelessWidget { color: cs.primaryContainer, shape: BoxShape.circle, ), - child: ClipOval( - child: Image.asset(mascot, fit: BoxFit.cover), - ), + child: ClipOval(child: Image.asset(mascot, fit: BoxFit.cover)), ), const SizedBox(width: 10), Expanded( @@ -1026,10 +1018,7 @@ class _SectionCard extends StatelessWidget { ], ), const SizedBox(height: 12), - Padding( - padding: const EdgeInsets.only(right: 6), - child: child, - ), + Padding(padding: const EdgeInsets.only(right: 6), child: child), ], ), ); @@ -1114,10 +1103,7 @@ class _ErrorBlock extends StatelessWidget { final ColorScheme cs = Theme.of(context).colorScheme; return Padding( padding: const EdgeInsets.symmetric(vertical: 8), - child: Text( - message, - style: TextStyle(color: cs.error, fontSize: 12.5), - ), + child: Text(message, style: TextStyle(color: cs.error, fontSize: 12.5)), ); } } diff --git a/lib/features/home/presentation/screens/learning_progress_screen.dart b/lib/features/home/presentation/screens/learning_progress_screen.dart index bc00cfb..9c6140a 100644 --- a/lib/features/home/presentation/screens/learning_progress_screen.dart +++ b/lib/features/home/presentation/screens/learning_progress_screen.dart @@ -34,17 +34,21 @@ class LearningProgressScreen extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final Map progressMap = ref.watch(lessonProgressNotifierProvider).value ?? - {}; + {}; - final int completed = - progressMap.values.where((LessonProgress p) => p.completed).length; + final int completed = progressMap.values + .where((LessonProgress p) => p.completed) + .length; final int total = _lessonOrder.length; // Find the next lesson to work on - final String? nextLessonId = _lessonOrder.firstWhere( - (String id) => progressMap[id]?.status != LessonStatus.completed, - orElse: () => '', - ).isEmpty + final String? nextLessonId = + _lessonOrder + .firstWhere( + (String id) => progressMap[id]?.status != LessonStatus.completed, + orElse: () => '', + ) + .isEmpty ? null : _lessonOrder.firstWhere( (String id) => progressMap[id]?.status != LessonStatus.completed, @@ -62,14 +66,15 @@ class LearningProgressScreen extends ConsumerWidget { _OverallRingCard(completed: completed, total: total), const SizedBox(height: 20), ..._lessonOrder.asMap().entries.map( - (MapEntry e) => Padding( - padding: const EdgeInsets.only(bottom: 12), - child: _LessonTile( - index: e.key, - lessonId: e.value, - lessonName: _lessonNames[e.value] ?? e.value, - progress: progressMap[e.value], - ) + (MapEntry e) => Padding( + padding: const EdgeInsets.only(bottom: 12), + child: + _LessonTile( + index: e.key, + lessonId: e.value, + lessonName: _lessonNames[e.value] ?? e.value, + progress: progressMap[e.value], + ) .animate(delay: (e.key * 60).ms) .fadeIn(duration: 280.ms) .slideX( @@ -78,8 +83,8 @@ class LearningProgressScreen extends ConsumerWidget { duration: 280.ms, curve: Curves.easeOutCubic, ), - ), - ), + ), + ), if (nextLessonId != null) ...[ const SizedBox(height: 8), _ButtyCtaCard(lessonId: nextLessonId) @@ -112,9 +117,13 @@ class _HeroAppBar extends StatelessWidget { final int total; String get _message { - if (completed == 0) return 'Simulan na natin!\nStart your Baybayin journey.'; + if (completed == 0) { + return 'Simulan na natin!\nStart your Baybayin journey.'; + } if (completed == total) return 'Magaling ka!\nAll lessons complete!'; - if (completed / total >= 0.5) return 'Halos tapos na!\nAlmost there, keep going!'; + if (completed / total >= 0.5) { + return 'Halos tapos na!\nAlmost there, keep going!'; + } return 'Magpatuloy lang!\nYou\'re making great progress.'; } @@ -205,19 +214,20 @@ class _HeroBanner extends StatelessWidget { Positioned( right: -8, bottom: -4, - child: Image.asset( - 'assets/brand/ButtyRead.webp', - height: 140, - fit: BoxFit.fitHeight, - ) - .animate(delay: 100.ms) - .slideX( - begin: 0.2, - end: 0, - duration: 400.ms, - curve: Curves.easeOutCubic, - ) - .fadeIn(duration: 300.ms), + child: + Image.asset( + 'assets/brand/ButtyRead.webp', + height: 140, + fit: BoxFit.fitHeight, + ) + .animate(delay: 100.ms) + .slideX( + begin: 0.2, + end: 0, + duration: 400.ms, + curve: Curves.easeOutCubic, + ) + .fadeIn(duration: 300.ms), ), Padding( padding: const EdgeInsets.fromLTRB(20, 56, 140, 26), @@ -247,14 +257,14 @@ class _HeroBanner extends StatelessWidget { ), const SizedBox(height: 8), Text( - message, - style: const TextStyle( - fontSize: 15, - fontWeight: FontWeight.w700, - color: Colors.white, - height: 1.4, - ), - ) + message, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w700, + color: Colors.white, + height: 1.4, + ), + ) .animate(delay: 80.ms) .fadeIn(duration: 300.ms) .slideY(begin: 0.1, end: 0, duration: 300.ms), @@ -278,17 +288,12 @@ class _Bubble extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - width: size, - height: size, - decoration: BoxDecoration(color: color, shape: BoxShape.circle), - ) + width: size, + height: size, + decoration: BoxDecoration(color: color, shape: BoxShape.circle), + ) .animate(onPlay: (AnimationController c) => c.repeat(reverse: true)) - .moveY( - begin: 0, - end: -8, - duration: 2000.ms, - curve: Curves.easeInOut, - ); + .moveY(begin: 0, end: -8, duration: 2000.ms, curve: Curves.easeInOut); } } @@ -334,63 +339,70 @@ class _OverallRingCard extends StatelessWidget { Widget build(BuildContext context) { final ColorScheme cs = Theme.of(context).colorScheme; return Container( - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 20), - decoration: BoxDecoration( - color: cs.surfaceContainerLow, - borderRadius: BorderRadius.circular(20), - border: Border.all(color: cs.outlineVariant), - ), - child: Row( - children: [ - _RingWidget(completed: completed, total: total, cs: cs), - const SizedBox(width: 20), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '$completed of $total lessons', - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.w800, - height: 1.1, - ), - ), - const SizedBox(height: 4), - Text( - 'completed', - style: TextStyle( - fontSize: 13, - color: cs.onSurface.withAlpha(140), - ), - ), - const SizedBox(height: 12), - ClipRRect( - borderRadius: BorderRadius.circular(6), - child: TweenAnimationBuilder( - tween: Tween( - end: total == 0 ? 0 : completed / total, + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 20), + decoration: BoxDecoration( + color: cs.surfaceContainerLow, + borderRadius: BorderRadius.circular(20), + border: Border.all(color: cs.outlineVariant), + ), + child: Row( + children: [ + _RingWidget(completed: completed, total: total, cs: cs), + const SizedBox(width: 20), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '$completed of $total lessons', + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.w800, + height: 1.1, + ), + ), + const SizedBox(height: 4), + Text( + 'completed', + style: TextStyle( + fontSize: 13, + color: cs.onSurface.withAlpha(140), + ), ), - duration: const Duration(milliseconds: 700), - curve: Curves.easeOutCubic, - builder: (BuildContext context, double v, _) => - LinearProgressIndicator( - value: v, - minHeight: 7, - backgroundColor: cs.surfaceContainerHigh, - valueColor: AlwaysStoppedAnimation(cs.primary), + const SizedBox(height: 12), + ClipRRect( + borderRadius: BorderRadius.circular(6), + child: TweenAnimationBuilder( + tween: Tween( + end: total == 0 ? 0 : completed / total, ), - ), + duration: const Duration(milliseconds: 700), + curve: Curves.easeOutCubic, + builder: (BuildContext context, double v, _) => + LinearProgressIndicator( + value: v, + minHeight: 7, + backgroundColor: cs.surfaceContainerHigh, + valueColor: AlwaysStoppedAnimation( + cs.primary, + ), + ), + ), + ), + ], ), - ], - ), + ), + ], ), - ], - ), - ) + ) .animate() .fadeIn(duration: 300.ms) - .slideY(begin: 0.1, end: 0, duration: 300.ms, curve: Curves.easeOutCubic); + .slideY( + begin: 0.1, + end: 0, + duration: 300.ms, + curve: Curves.easeOutCubic, + ); } } @@ -418,9 +430,7 @@ class _RingWidget extends StatelessWidget { child: CustomPaint( painter: _RingPainter( fraction: value, - arcColor: completed == total - ? const Color(0xFF46B986) - : cs.primary, + arcColor: completed == total ? const Color(0xFF46B986) : cs.primary, trackColor: cs.surfaceContainerHighest, ), child: Center( @@ -514,9 +524,10 @@ class _LessonTileState extends State<_LessonTile> duration: const Duration(milliseconds: 80), reverseDuration: const Duration(milliseconds: 180), ); - _scaleAnim = Tween(begin: 1.0, end: 0.97).animate( - CurvedAnimation(parent: _pressCtrl, curve: Curves.easeInOut), - ); + _scaleAnim = Tween( + begin: 1.0, + end: 0.97, + ).animate(CurvedAnimation(parent: _pressCtrl, curve: Curves.easeInOut)); } @override diff --git a/lib/features/home/presentation/screens/model_setup_screen.dart b/lib/features/home/presentation/screens/model_setup_screen.dart index 9f06e27..96a4ca7 100644 --- a/lib/features/home/presentation/screens/model_setup_screen.dart +++ b/lib/features/home/presentation/screens/model_setup_screen.dart @@ -114,10 +114,7 @@ class _SetupBackground extends StatelessWidget { // Faded Baybayin glyphs const BaybayinBackdrop(), // Soft radial aura — larger on desktop for a more dramatic backdrop - _AuraGlow( - size: isDesktop ? 520 : 280, - top: isDesktop ? -100 : -40, - ), + _AuraGlow(size: isDesktop ? 520 : 280, top: isDesktop ? -100 : -40), ], ); } @@ -174,8 +171,7 @@ class _ModelSetupBody extends StatelessWidget { builder: (BuildContext context, BoxConstraints constraints) { final bool isDesktop = constraints.maxWidth >= 900; final bool landscape = constraints.maxWidth > constraints.maxHeight; - final bool shortPortrait = - !landscape && constraints.maxHeight < 680; + final bool shortPortrait = !landscape && constraints.maxHeight < 680; if (isDesktop) { return _DesktopSetupLayout( busy: busy, @@ -330,7 +326,6 @@ class _DesktopDivider extends StatelessWidget { } } - class _PortraitSetupLayout extends StatelessWidget { const _PortraitSetupLayout({ required this.busy, @@ -524,19 +519,37 @@ class _SetupHeadline extends StatelessWidget { 'Get ready to use Kudlit', style: TextStyle( color: KudlitColors.blue900, - fontSize: large ? 32 : compact ? 24 : 28, + fontSize: large + ? 32 + : compact + ? 24 + : 28, fontWeight: FontWeight.w700, ), ), - SizedBox(height: large ? 14 : compact ? 8 : 10), + SizedBox( + height: large + ? 14 + : compact + ? 8 + : 10, + ), Text( kIsWeb ? 'Set up the downloads Kudlit needs before you start.' : 'Download these once so key features can keep working even without internet.', style: TextStyle( color: KudlitColors.grey300, - fontSize: large ? 16 : compact ? 13 : 15, - height: large ? 1.6 : compact ? 1.35 : 1.55, + fontSize: large + ? 16 + : compact + ? 13 + : 15, + height: large + ? 1.6 + : compact + ? 1.35 + : 1.55, ), ), ], diff --git a/lib/features/home/presentation/widgets/settings/activity_section.dart b/lib/features/home/presentation/widgets/settings/activity_section.dart index e7bf4e1..aa3d003 100644 --- a/lib/features/home/presentation/widgets/settings/activity_section.dart +++ b/lib/features/home/presentation/widgets/settings/activity_section.dart @@ -50,8 +50,7 @@ class ActivitySection extends ConsumerWidget { title: 'Learning progress', subtitle: 'Lessons, milestones, and streaks.', trailingLabel: lessons > 0 ? '$lessons done' : null, - onTap: () => - context.push(AppConstants.routeLearningProgress), + onTap: () => context.push(AppConstants.routeLearningProgress), ), const SettingsDivider(), ProfileNavRow( @@ -69,8 +68,7 @@ class ActivitySection extends ConsumerWidget { trailingLabel: (translations > 0 || bookmarks > 0) ? '$translations · $bookmarks saved' : null, - onTap: () => - context.push(AppConstants.routeTranslationHistory), + onTap: () => context.push(AppConstants.routeTranslationHistory), ), const SettingsDivider(), ProfileNavRow( diff --git a/lib/features/home/presentation/widgets/settings/personalization_section.dart b/lib/features/home/presentation/widgets/settings/personalization_section.dart index b3b853a..800ede1 100644 --- a/lib/features/home/presentation/widgets/settings/personalization_section.dart +++ b/lib/features/home/presentation/widgets/settings/personalization_section.dart @@ -74,13 +74,13 @@ class _PersonalizationSectionState passwordResetEmailSent: () => 'Email sent', ); } - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('Update failed: $message')), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text('Update failed: $message'))); } else { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Preferences saved.')), - ); + ScaffoldMessenger.of( + context, + ).showSnackBar(const SnackBar(content: Text('Preferences saved.'))); } } diff --git a/lib/features/home/presentation/widgets/settings/profile_management_card.dart b/lib/features/home/presentation/widgets/settings/profile_management_card.dart index 2e94510..2977421 100644 --- a/lib/features/home/presentation/widgets/settings/profile_management_card.dart +++ b/lib/features/home/presentation/widgets/settings/profile_management_card.dart @@ -24,10 +24,9 @@ class ProfileManagementCard extends StatelessWidget { for (int i = 0; i < items.length; i++) ...[ ProfileManagementTile( item: items[i], - isPrimaryLoading: loadingActions.contains( - items[i].primaryActionId, - ), - isSecondaryLoading: items[i].secondaryActionId != null && + isPrimaryLoading: loadingActions.contains(items[i].primaryActionId), + isSecondaryLoading: + items[i].secondaryActionId != null && loadingActions.contains(items[i].secondaryActionId!), onPrimaryTap: () => onAction( items[i].primaryActionId, diff --git a/lib/features/home/presentation/widgets/settings/profile_nav_row.dart b/lib/features/home/presentation/widgets/settings/profile_nav_row.dart index 7d63da0..1561902 100644 --- a/lib/features/home/presentation/widgets/settings/profile_nav_row.dart +++ b/lib/features/home/presentation/widgets/settings/profile_nav_row.dart @@ -28,8 +28,8 @@ class ProfileNavRow extends StatelessWidget { final Color fg = isDestructive ? cs.error : isSoon - ? cs.onSurface.withAlpha(110) - : cs.onSurface; + ? cs.onSurface.withAlpha(110) + : cs.onSurface; return InkWell( onTap: onTap, diff --git a/lib/features/home/presentation/widgets/settings/profile_stats_bar.dart b/lib/features/home/presentation/widgets/settings/profile_stats_bar.dart index 092ef14..1ccaaca 100644 --- a/lib/features/home/presentation/widgets/settings/profile_stats_bar.dart +++ b/lib/features/home/presentation/widgets/settings/profile_stats_bar.dart @@ -18,9 +18,15 @@ class ProfileStatsBar extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - Expanded(child: _Stat(count: lessons, label: 'Lessons')), - Expanded(child: _Stat(count: scans, label: 'Scans')), - Expanded(child: _Stat(count: translations, label: 'Translated')), + Expanded( + child: _Stat(count: lessons, label: 'Lessons'), + ), + Expanded( + child: _Stat(count: scans, label: 'Scans'), + ), + Expanded( + child: _Stat(count: translations, label: 'Translated'), + ), ], ); } diff --git a/lib/features/home/presentation/widgets/translate/translate_feedback_card.dart b/lib/features/home/presentation/widgets/translate/translate_feedback_card.dart index 1c91b04..e6aefce 100644 --- a/lib/features/home/presentation/widgets/translate/translate_feedback_card.dart +++ b/lib/features/home/presentation/widgets/translate/translate_feedback_card.dart @@ -119,10 +119,7 @@ MarkdownStyleSheet _cardMarkdownStyle(ColorScheme cs) { strong: base.copyWith(fontWeight: FontWeight.w700), em: base.copyWith(fontStyle: FontStyle.italic), listBullet: base, - a: base.copyWith( - color: cs.primary, - decoration: TextDecoration.underline, - ), + a: base.copyWith(color: cs.primary, decoration: TextDecoration.underline), code: base.copyWith( fontFamily: 'monospace', fontSize: 12, diff --git a/lib/features/home/presentation/widgets/translate/translate_sketchpad_mode_panel.dart b/lib/features/home/presentation/widgets/translate/translate_sketchpad_mode_panel.dart index b828092..ed7740c 100644 --- a/lib/features/home/presentation/widgets/translate/translate_sketchpad_mode_panel.dart +++ b/lib/features/home/presentation/widgets/translate/translate_sketchpad_mode_panel.dart @@ -270,11 +270,7 @@ class _BottomBar extends StatelessWidget { ), ), const SizedBox(width: 8), - _PillButton( - label: 'Clear', - enabled: hasStrokes, - onTap: onClear, - ), + _PillButton(label: 'Clear', enabled: hasStrokes, onTap: onClear), const SizedBox(width: 8), _PillButton( label: state.aiBusy ? 'Working...' : 'Get Feedback', @@ -425,10 +421,7 @@ MarkdownStyleSheet _feedbackMarkdownStyle(ColorScheme cs) { strong: base.copyWith(fontWeight: FontWeight.w700), em: base.copyWith(fontStyle: FontStyle.italic), listBullet: base, - a: base.copyWith( - color: cs.primary, - decoration: TextDecoration.underline, - ), + a: base.copyWith(color: cs.primary, decoration: TextDecoration.underline), code: base.copyWith( fontFamily: 'monospace', fontSize: 12.5, @@ -463,13 +456,14 @@ class _ThinkingDotsState extends State<_ThinkingDots> @override void initState() { super.initState(); - _ctrl = AnimationController( - vsync: this, - duration: const Duration(milliseconds: 500), - )..addListener(() { - final int next = (_ctrl.value * 3).floor() + 1; - if (next != _dotCount) setState(() => _dotCount = next); - }); + _ctrl = + AnimationController( + vsync: this, + duration: const Duration(milliseconds: 500), + )..addListener(() { + final int next = (_ctrl.value * 3).floor() + 1; + if (next != _dotCount) setState(() => _dotCount = next); + }); _ctrl.repeat(); } diff --git a/lib/features/learning/presentation/providers/character_gallery_provider.dart b/lib/features/learning/presentation/providers/character_gallery_provider.dart index f175abd..998181e 100644 --- a/lib/features/learning/presentation/providers/character_gallery_provider.dart +++ b/lib/features/learning/presentation/providers/character_gallery_provider.dart @@ -25,21 +25,26 @@ Future> characterGallery(Ref ref) async { if (seen.add(glyph)) unique.add(row); } - final List glyphs = - unique.map((Map r) => r['glyph'] as String).toList(); - final Map strokeOrders = - await _fetchStrokeOrders(client, glyphs); + final List glyphs = unique + .map((Map r) => r['glyph'] as String) + .toList(); + final Map strokeOrders = await _fetchStrokeOrders( + client, + glyphs, + ); - return unique.map((Map row) { - final String glyph = row['glyph'] as String; - final String lessonId = (row['lesson_id'] as String?) ?? ''; - return GlyphEntry( - glyph: glyph, - label: (row['label'] as String?) ?? glyph, - group: _groupFromLessonId(lessonId), - strokeOrder: strokeOrders[glyph], - ); - }).toList(growable: false); + return unique + .map((Map row) { + final String glyph = row['glyph'] as String; + final String lessonId = (row['lesson_id'] as String?) ?? ''; + return GlyphEntry( + glyph: glyph, + label: (row['label'] as String?) ?? glyph, + group: _groupFromLessonId(lessonId), + strokeOrder: strokeOrders[glyph], + ); + }) + .toList(growable: false); } Future> _fetchStrokeOrders( diff --git a/lib/features/learning/presentation/providers/lesson_controller.dart b/lib/features/learning/presentation/providers/lesson_controller.dart index 24de60f..a515428 100644 --- a/lib/features/learning/presentation/providers/lesson_controller.dart +++ b/lib/features/learning/presentation/providers/lesson_controller.dart @@ -47,8 +47,8 @@ class LessonController extends _$LessonController { .forLesson(lessonId); final int startIndex = (saved != null && !saved.completed && lesson.steps.isNotEmpty) - ? saved.currentStepIndex.clamp(0, lesson.steps.length - 1) - : 0; + ? saved.currentStepIndex.clamp(0, lesson.steps.length - 1) + : 0; return AsyncData( LessonState( lesson: lesson, @@ -95,7 +95,8 @@ class LessonController extends _$LessonController { .where((String p) => p.isNotEmpty) .toList(); final bool isCorrect = parts.any((String p) => step.expected.contains(p)); - final bool isFirstAttempt = current.attemptStatus == AttemptStatus.idle || + final bool isFirstAttempt = + current.attemptStatus == AttemptStatus.idle || current.attemptStatus == AttemptStatus.checking; state = AsyncData( current.copyWith( @@ -238,17 +239,19 @@ class LessonController extends _$LessonController { ); state = AsyncData(completed); unawaited( - ref.read(lessonProgressNotifierProvider.notifier).saveProgress( - LessonProgress( - lessonId: completed.lesson.id, - currentStepIndex: completed.lesson.steps.length, - totalSteps: completed.lesson.steps.length, - completed: true, - score: completed.score, - lastModified: DateTime.now(), - completedAt: DateTime.now(), - ), - ), + ref + .read(lessonProgressNotifierProvider.notifier) + .saveProgress( + LessonProgress( + lessonId: completed.lesson.id, + currentStepIndex: completed.lesson.steps.length, + totalSteps: completed.lesson.steps.length, + completed: true, + score: completed.score, + lastModified: DateTime.now(), + completedAt: DateTime.now(), + ), + ), ); unawaited(_saveLessonProgress(completed)); return; @@ -262,16 +265,18 @@ class LessonController extends _$LessonController { ), ); unawaited( - ref.read(lessonProgressNotifierProvider.notifier).saveProgress( - LessonProgress( - lessonId: current.lesson.id, - currentStepIndex: nextIndex, - totalSteps: current.lesson.steps.length, - completed: false, - score: 0, - lastModified: DateTime.now(), - ), - ), + ref + .read(lessonProgressNotifierProvider.notifier) + .saveProgress( + LessonProgress( + lessonId: current.lesson.id, + currentStepIndex: nextIndex, + totalSteps: current.lesson.steps.length, + completed: false, + score: 0, + lastModified: DateTime.now(), + ), + ), ); } @@ -290,16 +295,18 @@ class LessonController extends _$LessonController { final LessonState? current = state.value; if (current == null) return; unawaited( - ref.read(lessonProgressNotifierProvider.notifier).saveProgress( - LessonProgress( - lessonId: current.lesson.id, - currentStepIndex: 0, - totalSteps: current.lesson.steps.length, - completed: false, - score: 0, - lastModified: DateTime.now(), - ), - ), + ref + .read(lessonProgressNotifierProvider.notifier) + .saveProgress( + LessonProgress( + lessonId: current.lesson.id, + currentStepIndex: 0, + totalSteps: current.lesson.steps.length, + completed: false, + score: 0, + lastModified: DateTime.now(), + ), + ), ); state = AsyncData( LessonState( diff --git a/lib/features/learning/presentation/providers/lesson_progress_provider.dart b/lib/features/learning/presentation/providers/lesson_progress_provider.dart index fb8ebb9..ba9dd0b 100644 --- a/lib/features/learning/presentation/providers/lesson_progress_provider.dart +++ b/lib/features/learning/presentation/providers/lesson_progress_provider.dart @@ -49,9 +49,7 @@ class LessonProgressNotifier return _toMap(remote); } } catch (e) { - debugPrint( - '[LessonProgress] cloud restore failed (non-fatal): $e', - ); + debugPrint('[LessonProgress] cloud restore failed (non-fatal): $e'); } } } @@ -97,20 +95,17 @@ class LessonProgressNotifier final String? userId = client.auth.currentUser?.id; if (userId == null) return; try { - await client.from('learning_progress').upsert( - { - 'user_id': userId, - 'lesson_id': progress.lessonId, - 'current_step': progress.currentStepIndex, - 'total_steps': progress.totalSteps, - 'completed': progress.completed, - 'score': progress.score, - 'updated_at': progress.lastModified.toIso8601String(), - if (progress.completedAt != null) - 'completed_at': progress.completedAt!.toIso8601String(), - }, - onConflict: 'user_id,lesson_id', - ); + await client.from('learning_progress').upsert({ + 'user_id': userId, + 'lesson_id': progress.lessonId, + 'current_step': progress.currentStepIndex, + 'total_steps': progress.totalSteps, + 'completed': progress.completed, + 'score': progress.score, + 'updated_at': progress.lastModified.toIso8601String(), + if (progress.completedAt != null) + 'completed_at': progress.completedAt!.toIso8601String(), + }, onConflict: 'user_id,lesson_id'); } catch (e) { debugPrint('[LessonProgress] Supabase sync failed (non-fatal): $e'); } @@ -129,22 +124,25 @@ class LessonProgressNotifier .order('updated_at', ascending: false) .limit(20); - return rows.map((dynamic row) { - final Map r = row as Map; - final String? completedAtStr = r['completed_at'] as String?; - final String updatedAtStr = - r['updated_at'] as String? ?? DateTime.now().toIso8601String(); - return LessonProgress( - lessonId: r['lesson_id'] as String, - currentStepIndex: r['current_step'] as int? ?? 0, - totalSteps: r['total_steps'] as int? ?? 0, - completed: r['completed'] as bool? ?? false, - score: r['score'] as int? ?? 0, - lastModified: DateTime.parse(updatedAtStr), - completedAt: - completedAtStr != null ? DateTime.parse(completedAtStr) : null, - ); - }).toList(growable: false); + return rows + .map((dynamic row) { + final Map r = row as Map; + final String? completedAtStr = r['completed_at'] as String?; + final String updatedAtStr = + r['updated_at'] as String? ?? DateTime.now().toIso8601String(); + return LessonProgress( + lessonId: r['lesson_id'] as String, + currentStepIndex: r['current_step'] as int? ?? 0, + totalSteps: r['total_steps'] as int? ?? 0, + completed: r['completed'] as bool? ?? false, + score: r['score'] as int? ?? 0, + lastModified: DateTime.parse(updatedAtStr), + completedAt: completedAtStr != null + ? DateTime.parse(completedAtStr) + : null, + ); + }) + .toList(growable: false); } static Map _toMap(List list) { diff --git a/lib/features/learning/presentation/providers/quiz_provider.dart b/lib/features/learning/presentation/providers/quiz_provider.dart index d8cee3b..79543d2 100644 --- a/lib/features/learning/presentation/providers/quiz_provider.dart +++ b/lib/features/learning/presentation/providers/quiz_provider.dart @@ -107,7 +107,9 @@ class QuizNotifier extends _$QuizNotifier { state = AsyncData( current.copyWith( status: isCorrect ? QuizStatus.correct : QuizStatus.wrong, - correctCount: isCorrect ? current.correctCount + 1 : current.correctCount, + correctCount: isCorrect + ? current.correctCount + 1 + : current.correctCount, ), ); } @@ -125,10 +127,7 @@ class QuizNotifier extends _$QuizNotifier { } state = AsyncData( - current.copyWith( - currentIndex: nextIndex, - status: QuizStatus.answering, - ), + current.copyWith(currentIndex: nextIndex, status: QuizStatus.answering), ); } diff --git a/lib/features/scanner/data/datasources/sqlite_scan_history_datasource.dart b/lib/features/scanner/data/datasources/sqlite_scan_history_datasource.dart index 1b93bc9..b93d77e 100644 --- a/lib/features/scanner/data/datasources/sqlite_scan_history_datasource.dart +++ b/lib/features/scanner/data/datasources/sqlite_scan_history_datasource.dart @@ -101,9 +101,7 @@ class SqliteScanHistoryDatasource { id: row['id'] as int?, tokens: decoded.cast(), translation: row['translation'] as String, - timestamp: DateTime.fromMillisecondsSinceEpoch( - row['timestamp'] as int, - ), + timestamp: DateTime.fromMillisecondsSinceEpoch(row['timestamp'] as int), ); } } diff --git a/lib/features/scanner/presentation/providers/scan_history_provider.dart b/lib/features/scanner/presentation/providers/scan_history_provider.dart index bd83323..0692a6c 100644 --- a/lib/features/scanner/presentation/providers/scan_history_provider.dart +++ b/lib/features/scanner/presentation/providers/scan_history_provider.dart @@ -9,12 +9,14 @@ import 'package:kudlit_ph/features/home/presentation/providers/profile_managemen import 'package:kudlit_ph/features/scanner/data/datasources/sqlite_scan_history_datasource.dart'; import 'package:kudlit_ph/features/scanner/domain/entities/scan_result.dart'; -final Provider sqliteScanHistoryDatasourceProvider = - Provider((Ref ref) { - final SqliteScanHistoryDatasource ds = SqliteScanHistoryDatasource(); - ref.onDispose(ds.dispose); - return ds; - }); +final Provider +sqliteScanHistoryDatasourceProvider = Provider(( + Ref ref, +) { + final SqliteScanHistoryDatasource ds = SqliteScanHistoryDatasource(); + ref.onDispose(ds.dispose); + return ds; +}); final AsyncNotifierProvider> scanHistoryNotifierProvider = @@ -101,17 +103,20 @@ class ScanHistoryNotifier extends AsyncNotifier> { .order('scanned_at', ascending: false) .limit(100); - return rows.map((dynamic row) { - final Map r = row as Map; - final dynamic rawTokens = r['tokens']; - final List tokens = rawTokens is List - ? rawTokens.cast() - : (jsonDecode(rawTokens as String) as List).cast(); - return ScanResult( - tokens: tokens, - translation: r['translation'] as String? ?? '', - timestamp: DateTime.parse(r['scanned_at'] as String), - ); - }).toList(growable: false); + return rows + .map((dynamic row) { + final Map r = row as Map; + final dynamic rawTokens = r['tokens']; + final List tokens = rawTokens is List + ? rawTokens.cast() + : (jsonDecode(rawTokens as String) as List) + .cast(); + return ScanResult( + tokens: tokens, + translation: r['translation'] as String? ?? '', + timestamp: DateTime.parse(r['scanned_at'] as String), + ); + }) + .toList(growable: false); } } diff --git a/lib/features/scanner/presentation/providers/scanner_provider.g.dart b/lib/features/scanner/presentation/providers/scanner_provider.g.dart index 14aa712..01d5195 100644 --- a/lib/features/scanner/presentation/providers/scanner_provider.g.dart +++ b/lib/features/scanner/presentation/providers/scanner_provider.g.dart @@ -33,10 +33,7 @@ String _$scannerNotifierHash() => r'scannerNotifier'; /// Copied from [ScannerNotifier]. @ProviderFor(ScannerNotifier) final scannerNotifierProvider = - NotifierProvider< - ScannerNotifier, - List - >.internal( + NotifierProvider>.internal( ScannerNotifier.new, name: r'scannerNotifierProvider', from: null, diff --git a/lib/features/scanner/presentation/widgets/aggregated_bounding_box.dart b/lib/features/scanner/presentation/widgets/aggregated_bounding_box.dart index 822324d..da06692 100644 --- a/lib/features/scanner/presentation/widgets/aggregated_bounding_box.dart +++ b/lib/features/scanner/presentation/widgets/aggregated_bounding_box.dart @@ -84,11 +84,11 @@ class AggregatedBoundingBox extends StatelessWidget { } static Rect _toPixels(BaybayinDetection d, Size size) => Rect.fromLTWH( - d.left * size.width, - d.top * size.height, - d.width * size.width, - d.height * size.height, - ); + d.left * size.width, + d.top * size.height, + d.width * size.width, + d.height * size.height, + ); } // ── Box painter ─────────────────────────────────────────────────────────────── @@ -147,11 +147,7 @@ class _ChipPositioner extends StatelessWidget { return Positioned( left: outer.left.clamp(4.0, size.width - 80), top: top, - child: _PermutationChip( - primary: primary, - extras: extras, - onTap: onTap, - ), + child: _PermutationChip(primary: primary, extras: extras, onTap: onTap), ); } } diff --git a/lib/features/translator/data/datasources/sqlite_chat_datasource.dart b/lib/features/translator/data/datasources/sqlite_chat_datasource.dart index 2a4f8a5..a4c2cef 100644 --- a/lib/features/translator/data/datasources/sqlite_chat_datasource.dart +++ b/lib/features/translator/data/datasources/sqlite_chat_datasource.dart @@ -96,7 +96,10 @@ class SqliteChatDatasource { } /// Attach the Supabase UUID to a previously-inserted local row. - Future setRemoteId({required int localId, required String remoteId}) async { + Future setRemoteId({ + required int localId, + required String remoteId, + }) async { try { final Database db = await _open(); await db.update( diff --git a/lib/features/translator/data/datasources/sqlite_chat_memory_datasource.dart b/lib/features/translator/data/datasources/sqlite_chat_memory_datasource.dart index 3ec9fb9..eec267e 100644 --- a/lib/features/translator/data/datasources/sqlite_chat_memory_datasource.dart +++ b/lib/features/translator/data/datasources/sqlite_chat_memory_datasource.dart @@ -195,9 +195,7 @@ class SqliteChatMemoryDatasource { remoteId: row['remote_id'] as String?, factType: row['fact_type'] as String, content: row['content'] as String, - createdAt: DateTime.fromMillisecondsSinceEpoch( - row['created_at'] as int, - ), + createdAt: DateTime.fromMillisecondsSinceEpoch(row['created_at'] as int), lastReferencedAt: DateTime.fromMillisecondsSinceEpoch( row['last_referenced_at'] as int, ), diff --git a/lib/features/translator/data/datasources/supabase_chat_datasource.dart b/lib/features/translator/data/datasources/supabase_chat_datasource.dart index 9225868..1eef40b 100644 --- a/lib/features/translator/data/datasources/supabase_chat_datasource.dart +++ b/lib/features/translator/data/datasources/supabase_chat_datasource.dart @@ -49,15 +49,17 @@ class SupabaseChatDatasource { .order('created_at', ascending: false) .limit(limit); - final List messages = rows.map((dynamic raw) { - final Map r = raw as Map; - return ChatMessage( - remoteId: r['id'] as String?, - text: r['content'] as String? ?? '', - isUser: r['is_user'] as bool? ?? false, - timestamp: DateTime.parse(r['created_at'] as String), - ); - }).toList(growable: false); + final List messages = rows + .map((dynamic raw) { + final Map r = raw as Map; + return ChatMessage( + remoteId: r['id'] as String?, + text: r['content'] as String? ?? '', + isUser: r['is_user'] as bool? ?? false, + timestamp: DateTime.parse(r['created_at'] as String), + ); + }) + .toList(growable: false); return messages.reversed.toList(growable: false); } catch (e) { diff --git a/lib/features/translator/data/datasources/supabase_chat_memory_datasource.dart b/lib/features/translator/data/datasources/supabase_chat_memory_datasource.dart index bba450b..ff19336 100644 --- a/lib/features/translator/data/datasources/supabase_chat_memory_datasource.dart +++ b/lib/features/translator/data/datasources/supabase_chat_memory_datasource.dart @@ -16,25 +16,25 @@ class SupabaseChatMemoryDatasource { try { final List rows = await _client .from(_table) - .select( - 'id, fact_type, content, created_at, last_referenced_at', - ) + .select('id, fact_type, content, created_at, last_referenced_at') .eq('user_id', userId) .order('created_at', ascending: false) .limit(limit); - return rows.map((dynamic raw) { - final Map r = raw as Map; - return ChatMemoryFact( - remoteId: r['id'] as String?, - factType: r['fact_type'] as String? ?? 'general', - content: r['content'] as String? ?? '', - createdAt: DateTime.parse(r['created_at'] as String), - lastReferencedAt: DateTime.parse( - r['last_referenced_at'] as String, - ), - ); - }).toList(growable: false); + return rows + .map((dynamic raw) { + final Map r = raw as Map; + return ChatMemoryFact( + remoteId: r['id'] as String?, + factType: r['fact_type'] as String? ?? 'general', + content: r['content'] as String? ?? '', + createdAt: DateTime.parse(r['created_at'] as String), + lastReferencedAt: DateTime.parse( + r['last_referenced_at'] as String, + ), + ); + }) + .toList(growable: false); } catch (e) { debugPrint('[ChatMemory] Supabase fetch failed (non-fatal): $e'); return const []; diff --git a/lib/features/translator/domain/entities/ai_model_info.dart b/lib/features/translator/domain/entities/ai_model_info.dart index 1724bc6..f03c7d3 100644 --- a/lib/features/translator/domain/entities/ai_model_info.dart +++ b/lib/features/translator/domain/entities/ai_model_info.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:meta/meta.dart'; /// Distinguishes the inference engine a model is built for. @@ -61,19 +59,6 @@ class AiModelInfo { /// [ModelKind.vision] → YOLO TFLite / mlpackage (OCR / camera pipeline). final ModelKind modelType; - /// Platform-appropriate download URL. - /// - /// Preference order: platform-specific link → generic [modelLink]. - String get platformLink { - if (Platform.isAndroid && androidModelLink != null) { - return androidModelLink!; - } - if (Platform.isIOS && iosModelLink != null) { - return iosModelLink!; - } - return modelLink; - } - /// Filename derived from [modelLink], used by `flutter_gemma` /// to check whether the model is already installed locally. String get fileName { diff --git a/lib/features/translator/presentation/providers/ai_inference_provider.g.dart b/lib/features/translator/presentation/providers/ai_inference_provider.g.dart index 183cc84..82e9182 100644 --- a/lib/features/translator/presentation/providers/ai_inference_provider.g.dart +++ b/lib/features/translator/presentation/providers/ai_inference_provider.g.dart @@ -6,7 +6,6 @@ part of 'ai_inference_provider.dart'; // RiverpodGenerator // ************************************************************************** - @ProviderFor(AiInferenceNotifier) final aiInferenceNotifierProvider = AsyncNotifierProvider.internal( diff --git a/lib/features/translator/presentation/providers/chat_history_provider.dart b/lib/features/translator/presentation/providers/chat_history_provider.dart index d6beb3d..bcfd991 100644 --- a/lib/features/translator/presentation/providers/chat_history_provider.dart +++ b/lib/features/translator/presentation/providers/chat_history_provider.dart @@ -42,7 +42,9 @@ class ChatHistoryNotifier extends _$ChatHistoryNotifier { final ChatMessage saved = await _local.insert(m); rehydrated.add(saved); } catch (e) { - debugPrint('[ChatHistory] cloud→local rehydrate failed (non-fatal): $e'); + debugPrint( + '[ChatHistory] cloud→local rehydrate failed (non-fatal): $e', + ); } } return rehydrated; diff --git a/lib/features/translator/presentation/providers/chat_history_provider.g.dart b/lib/features/translator/presentation/providers/chat_history_provider.g.dart index 3257db4..489b42c 100644 --- a/lib/features/translator/presentation/providers/chat_history_provider.g.dart +++ b/lib/features/translator/presentation/providers/chat_history_provider.g.dart @@ -6,7 +6,6 @@ part of 'chat_history_provider.dart'; // RiverpodGenerator // ************************************************************************** - @ProviderFor(ChatHistoryNotifier) final chatHistoryNotifierProvider = AsyncNotifierProvider>.internal( diff --git a/lib/features/translator/presentation/providers/chat_memory_provider.dart b/lib/features/translator/presentation/providers/chat_memory_provider.dart index f130b9f..8db56da 100644 --- a/lib/features/translator/presentation/providers/chat_memory_provider.dart +++ b/lib/features/translator/presentation/providers/chat_memory_provider.dart @@ -15,11 +15,11 @@ final Provider sqliteChatMemoryDatasourceProvider = }); final Provider -supabaseChatMemoryDatasourceProvider = Provider( - (Ref ref) { - return SupabaseChatMemoryDatasource(ref.watch(supabaseProvider)); - }, -); +supabaseChatMemoryDatasourceProvider = Provider(( + Ref ref, +) { + return SupabaseChatMemoryDatasource(ref.watch(supabaseProvider)); +}); final Provider chatMemoryRepositoryProvider = Provider((Ref ref) { diff --git a/lib/features/translator/presentation/providers/memory_extraction_service.dart b/lib/features/translator/presentation/providers/memory_extraction_service.dart index 399abb6..e4bcf63 100644 --- a/lib/features/translator/presentation/providers/memory_extraction_service.dart +++ b/lib/features/translator/presentation/providers/memory_extraction_service.dart @@ -63,10 +63,8 @@ class MemoryExtractionService { _running = true; _lastRun = now; try { - final List history = _ref - .read(chatHistoryNotifierProvider) - .value ?? - []; + final List history = + _ref.read(chatHistoryNotifierProvider).value ?? []; if (history.length < 2) return; final List window = history.length <= _windowSize @@ -82,10 +80,9 @@ class MemoryExtractionService { final Stream stream = _ref .read(aiInferenceNotifierProvider.notifier) - .generateResponse( - [userMessage], - systemInstruction: GemmaPrompts.memoryExtractor, - ); + .generateResponse([ + userMessage, + ], systemInstruction: GemmaPrompts.memoryExtractor); final StringBuffer buf = StringBuffer(); await for (final String chunk in stream) { @@ -98,9 +95,7 @@ class MemoryExtractionService { return; } debugPrint('[MemoryExtraction] extracted ${facts.length} fact(s)'); - await _ref - .read(chatMemoryNotifierProvider.notifier) - .addFacts(facts); + await _ref.read(chatMemoryNotifierProvider.notifier).addFacts(facts); } catch (e) { debugPrint('[MemoryExtraction] failed (non-fatal): $e'); } finally { diff --git a/lib/features/translator/presentation/providers/translator_providers.dart b/lib/features/translator/presentation/providers/translator_providers.dart index b7363ee..41f4aff 100644 --- a/lib/features/translator/presentation/providers/translator_providers.dart +++ b/lib/features/translator/presentation/providers/translator_providers.dart @@ -99,32 +99,32 @@ AiInferenceRepository aiInferenceRepository(Ref ref) { /// settings toggle. final FutureProvider localModelReadinessProvider = FutureProvider((Ref ref) async { - final String? selectedModelId = ref.watch( - appPreferencesNotifierProvider.select( - (AsyncValue v) => v.value?.selectedModelId, - ), - ); - final List models = await ref.read( - availableGemmaModelsProvider.future, - ); - if (models.isEmpty) { - return const LocalGemmaReadiness( - installed: false, - usable: false, - detail: 'Offline model is unavailable on this device.', - ); - } - GemmaModelInfo active = models[models.length ~/ 2]; - if (selectedModelId != null) { - for (final GemmaModelInfo m in models) { - if (m.id == selectedModelId) { - active = m; - break; + final String? selectedModelId = ref.watch( + appPreferencesNotifierProvider.select( + (AsyncValue v) => v.value?.selectedModelId, + ), + ); + final List models = await ref.read( + availableGemmaModelsProvider.future, + ); + if (models.isEmpty) { + return const LocalGemmaReadiness( + installed: false, + usable: false, + detail: 'Offline model is unavailable on this device.', + ); + } + GemmaModelInfo active = models[models.length ~/ 2]; + if (selectedModelId != null) { + for (final GemmaModelInfo m in models) { + if (m.id == selectedModelId) { + active = m; + break; + } + } } - } - } - return ref.read(localGemmaDatasourceProvider).probeReadiness(active); -}); + return ref.read(localGemmaDatasourceProvider).probeReadiness(active); + }); @Riverpod(keepAlive: true) AnalyzeBaybayinImage analyzeBaybayinImage(Ref ref) { diff --git a/lib/features/translator/presentation/providers/translator_providers.g.dart b/lib/features/translator/presentation/providers/translator_providers.g.dart index 20c64c4..2ae8e90 100644 --- a/lib/features/translator/presentation/providers/translator_providers.g.dart +++ b/lib/features/translator/presentation/providers/translator_providers.g.dart @@ -48,7 +48,6 @@ final supabaseGemmaModelsDatasourceProvider = // ignore: unused_element typedef SupabaseAiModelsDatasourceRef = Ref; - @ProviderFor(localGemmaDatasource) final localGemmaDatasourceProvider = Provider.internal( localGemmaDatasource, @@ -65,7 +64,6 @@ final localGemmaDatasourceProvider = Provider.internal( // ignore: unused_element typedef LocalGemmaDatasourceRef = Ref; - @ProviderFor(cloudGemmaDatasource) final cloudGemmaDatasourceProvider = Provider.internal( cloudGemmaDatasource, @@ -82,7 +80,6 @@ final cloudGemmaDatasourceProvider = Provider.internal( // ignore: unused_element typedef CloudGemmaDatasourceRef = Ref; - @ProviderFor(sqliteChatDatasource) final sqliteChatDatasourceProvider = Provider.internal( sqliteChatDatasource, @@ -99,7 +96,6 @@ final sqliteChatDatasourceProvider = Provider.internal( // ignore: unused_element typedef SqliteChatDatasourceRef = Ref; - @ProviderFor(aiInferenceRepository) final aiInferenceRepositoryProvider = Provider.internal( aiInferenceRepository, diff --git a/test/features/home/presentation/widgets/baybayin_chat_renderer_test.dart b/test/features/home/presentation/widgets/baybayin_chat_renderer_test.dart index 1148d91..658faa8 100644 --- a/test/features/home/presentation/widgets/baybayin_chat_renderer_test.dart +++ b/test/features/home/presentation/widgets/baybayin_chat_renderer_test.dart @@ -6,20 +6,15 @@ import 'package:kudlit_ph/features/home/presentation/widgets/butty_chat/baybayin const TextStyle _base = TextStyle(fontSize: 13.5, height: 1.5); Widget _wrap(Widget child) => MaterialApp( - home: Scaffold( - body: SizedBox(width: 300, child: child), - ), - ); + home: Scaffold(body: SizedBox(width: 300, child: child)), +); void main() { group('BaybayinChatRenderer — plain markdown', () { testWidgets('renders text without tags as markdown', (tester) async { await tester.pumpWidget( _wrap( - const BaybayinChatRenderer( - text: 'Hello **world**', - baseStyle: _base, - ), + const BaybayinChatRenderer(text: 'Hello **world**', baseStyle: _base), ), ); @@ -138,8 +133,7 @@ void main() { await tester.pumpWidget( _wrap( const BaybayinChatRenderer( - text: - 'mahal and salamat', + text: 'mahal and salamat', baseStyle: _base, ), ), @@ -194,10 +188,7 @@ void main() { w is Text && w.style?.fontFamily == 'Baybayin Simple TAWBID', ), ); - expect( - baybayinText.style!.fontSize, - greaterThan(baseFontSize), - ); + expect(baybayinText.style!.fontSize, greaterThan(baseFontSize)); }); }); }