From fcf1ec5cc820a27851b99f7c93d71df4c11a6e02 Mon Sep 17 00:00:00 2001 From: Fabian Freund Date: Fri, 25 Jul 2025 22:18:59 +0200 Subject: [PATCH] update onboarding ai features --- .../onboarding/presentation/onboarding.dart | 19 ++-- .../presentation/pages/ai_configuration.dart | 88 +++++++++++++++++++ .../user/domain/repositories/onboarding.dart | 2 +- .../domain/repositories/onboarding.g.dart | 2 +- 4 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 app/lib/features/onboarding/presentation/pages/ai_configuration.dart diff --git a/app/lib/features/onboarding/presentation/onboarding.dart b/app/lib/features/onboarding/presentation/onboarding.dart index c159aadc..3eede070 100644 --- a/app/lib/features/onboarding/presentation/onboarding.dart +++ b/app/lib/features/onboarding/presentation/onboarding.dart @@ -6,6 +6,7 @@ import 'package:weblibre/core/providers/defaults.dart'; import 'package:weblibre/core/providers/router.dart'; import 'package:weblibre/features/geckoview/domain/repositories/tab.dart'; import 'package:weblibre/features/onboarding/presentation/pages/abstract/i_form_page.dart'; +import 'package:weblibre/features/onboarding/presentation/pages/ai_configuration.dart'; import 'package:weblibre/features/onboarding/presentation/pages/default_search.dart'; import 'package:weblibre/features/onboarding/presentation/pages/permissions.dart'; import 'package:weblibre/features/onboarding/presentation/pages/ublock_opt_in.dart'; @@ -29,12 +30,18 @@ class OnboardingScreen extends HookConsumerWidget { final pageController = usePageController(); final pages = useMemoized>(() { - return [ - const WelcomePage(), - const DefaultSearchPage(), - UBlockOptInPage(formKey: GlobalKey()), - PermissionsPage(formKey: GlobalKey()), - ]; + switch (currentRevision) { + case 1: + return [const AiConfigurationPage()]; + default: + return [ + const WelcomePage(), + const DefaultSearchPage(), + const AiConfigurationPage(), + UBlockOptInPage(formKey: GlobalKey()), + PermissionsPage(formKey: GlobalKey()), + ]; + } }, [currentRevision, targetRevision]); final lastPage = useRef(pageController.initialPage); diff --git a/app/lib/features/onboarding/presentation/pages/ai_configuration.dart b/app/lib/features/onboarding/presentation/pages/ai_configuration.dart new file mode 100644 index 00000000..2d2eb51f --- /dev/null +++ b/app/lib/features/onboarding/presentation/pages/ai_configuration.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:weblibre/features/settings/presentation/controllers/save_settings.dart'; +import 'package:weblibre/features/user/data/models/general_settings.dart'; +import 'package:weblibre/features/user/domain/repositories/general_settings.dart'; + +class AiConfigurationPage extends HookConsumerWidget { + const AiConfigurationPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final theme = Theme.of(context); + + final enableLocalAiFeatures = ref.watch( + generalSettingsRepositoryProvider.select( + (settings) => settings.enableLocalAiFeatures, + ), + ); + + return ListView( + children: [ + const SizedBox(height: 24), + Center( + child: Text('AI Features', style: theme.textTheme.headlineMedium), + ), + const SizedBox(height: 24), + SwitchListTile.adaptive( + title: const Text('On Device AI'), + subtitle: const Text( + 'Local on-device features including container topic and tab suggestions', + ), + secondary: const Icon(MdiIcons.creation), + value: enableLocalAiFeatures, + onChanged: (value) async { + await ref + .read(saveGeneralSettingsControllerProvider.notifier) + .save( + (currentSettings) => + currentSettings.copyWith.enableLocalAiFeatures(value), + ); + }, + ), + Container( + width: double.infinity, + padding: const EdgeInsets.all(24.0), + margin: const EdgeInsets.symmetric(horizontal: 24, vertical: 24), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.error, + borderRadius: BorderRadius.circular(8.0), + ), + child: Text.rich( + TextSpan( + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colorScheme.onError, + height: 1.4, + ), + children: [ + TextSpan( + text: 'Things to keep in mind\n\n', + style: Theme.of(context).textTheme.headlineSmall?.copyWith( + color: Theme.of(context).colorScheme.onError, + fontWeight: FontWeight.w600, + ), + ), + const TextSpan(text: '• '), + const TextSpan( + text: + 'WebLibre uses a local AI model to read your open tab titles and suggest container tabs and names. Everything happens on your device.\n\n', + ), + const TextSpan(text: '• '), + const TextSpan( + text: + 'AI enhancements operate entirely within your browser, keeping all data on your device. Local AI processing respects your privacy and provides faster suggestions for container groups and names. You can control this behavior anytime through settings.\n\n', + ), + const TextSpan(text: '• '), + const TextSpan( + text: + 'AI can sometimes make mistakes, so please review suggested group names and tab selections.', + ), + ], + ), + ), + ), + ], + ); + } +} diff --git a/app/lib/features/user/domain/repositories/onboarding.dart b/app/lib/features/user/domain/repositories/onboarding.dart index 36b40fe2..b84c595c 100644 --- a/app/lib/features/user/domain/repositories/onboarding.dart +++ b/app/lib/features/user/domain/repositories/onboarding.dart @@ -5,7 +5,7 @@ part 'onboarding.g.dart'; @Riverpod(keepAlive: true) class OnboardingRepository extends _$OnboardingRepository { - static const targetRevision = 1; + static const targetRevision = 2; Future getCurrentRevision() { return ref diff --git a/app/lib/features/user/domain/repositories/onboarding.g.dart b/app/lib/features/user/domain/repositories/onboarding.g.dart index ad730489..1467476d 100644 --- a/app/lib/features/user/domain/repositories/onboarding.g.dart +++ b/app/lib/features/user/domain/repositories/onboarding.g.dart @@ -7,7 +7,7 @@ part of 'onboarding.dart'; // ************************************************************************** String _$onboardingRepositoryHash() => - r'37b76aa313ae6dfed6ab5695b4df8c4a0cfe0483'; + r'937a5fe08c09cc0475270d3707bfb6f087bcf123'; /// See also [OnboardingRepository]. @ProviderFor(OnboardingRepository)