import 'package:expandable_page_view/expandable_page_view.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:lensai/features/geckoview/features/browser/domain/providers.dart'; import 'package:lensai/features/geckoview/features/browser/presentation/widgets/sheets/create_tab.dart'; import 'package:lensai/features/geckoview/features/browser/presentation/widgets/speech_to_text_button.dart'; import 'package:lensai/features/kagi/data/entities/modes.dart'; import 'package:lensai/features/kagi/utils/url_builder.dart' as uri_builder; import 'package:lensai/features/settings/data/models/settings.dart'; import 'package:lensai/features/settings/data/repositories/settings_repository.dart'; import 'package:lensai/features/share_intent/domain/entities/shared_content.dart'; import 'package:lensai/presentation/hooks/sync_page_tab.dart'; class AssistantTab extends HookConsumerWidget { final SharedContent? sharedContent; final OnSubmitUri onSubmit; const AssistantTab({ required this.sharedContent, required this.onSubmit, super.key, }); @override Widget build(BuildContext context, WidgetRef ref) { final incognitoEnabled = ref.watch( settingsRepositoryProvider.select( (value) => (value.valueOrNull ?? Settings.withDefaults()).incognitoMode, ), ); final researchVariant = ref.watch(activeResearchVariantProvider); final chatModel = ref.watch(activeChatModelProvider); useAutomaticKeepAlive(); final formKey = useMemoized(() => GlobalKey()); final textController = useTextEditingController(text: sharedContent?.toString()); final tabController = useTabController( initialLength: AssistantMode.values.length, initialIndex: ref.read(lastUsedAssistantModeProvider).index, ); final pageController = usePageController(initialPage: tabController.index); useSyncPageWithTab( tabController, pageController, onIndexChanged: (index) { ref .read(lastUsedAssistantModeProvider.notifier) .update(AssistantMode.values[index]); }, ); return Form( key: formKey, child: Column( mainAxisSize: MainAxisSize.min, children: [ TabBar.secondary( controller: tabController, tabs: const [ Tab( text: 'Research', icon: Icon(MdiIcons.layersSearch), ), Tab( text: 'Code', icon: Icon(MdiIcons.codeJson), ), Tab( text: 'Chat', icon: Icon(MdiIcons.commentTextMultiple), ), Tab( text: 'Custom', icon: Icon(MdiIcons.creation), ), ], ), ExpandablePageView( controller: pageController, children: [ Column( mainAxisSize: MainAxisSize.min, children: [ const SizedBox( height: 12, ), SegmentedButton( segments: const [ ButtonSegment( value: ResearchVariant.expert, icon: Icon(MdiIcons.textSearch), label: Text('Research'), ), ButtonSegment( value: ResearchVariant.fast, icon: Icon(MdiIcons.invoiceTextFast), label: Text('Fast'), ), ], selected: {researchVariant}, onSelectionChanged: (value) { ref .read(activeResearchVariantProvider.notifier) .update(value.first); }, ), const SizedBox( height: 12, ), _PromptField(textController, incognitoEnabled), ], ), Column( mainAxisSize: MainAxisSize.min, children: [ const SizedBox( height: 12, ), _PromptField(textController, incognitoEnabled), ], ), Column( mainAxisSize: MainAxisSize.min, children: [ const SizedBox( height: 12, ), DropdownMenu( initialSelection: chatModel, expandedInsets: EdgeInsets.zero, label: const Text('Model'), inputDecorationTheme: const InputDecorationTheme(), dropdownMenuEntries: ChatModel.values .map( (model) => DropdownMenuEntry( value: model, label: model.label, ), ) .toList(), onSelected: (value) { ref.read(activeChatModelProvider.notifier).update(value!); }, ), const SizedBox( height: 12, ), _PromptField(textController, incognitoEnabled), ], ), Column( mainAxisSize: MainAxisSize.min, children: [ const SizedBox( height: 12, ), _PromptField(textController, incognitoEnabled), ], ), ], ), const SizedBox( height: 12, ), SizedBox( width: double.infinity, child: FilledButton.icon( onPressed: () { if (formKey.currentState?.validate() ?? false) { onSubmit( uri_builder.assistantUri( prompt: textController.text, assistantMode: AssistantMode.values[tabController.index], researchVariant: researchVariant, chatModel: chatModel, ), ); } }, label: const Text('Submit'), icon: const Icon(MdiIcons.invoiceTextSend), ), ), ], ), ); } } class _PromptField extends StatelessWidget { final TextEditingController textController; final bool incognitoEnabled; const _PromptField(this.textController, this.incognitoEnabled); @override Widget build(BuildContext context) { return TextFormField( controller: textController, enableIMEPersonalizedLearning: !incognitoEnabled, decoration: InputDecoration( label: const Text('Prompt'), hintText: 'Enter your prompt...', floatingLabelBehavior: FloatingLabelBehavior.always, suffixIcon: SpeechToTextButton( onTextReceived: (data) { textController.text = data.toString(); }, ), ), maxLines: null, validator: (value) { if (value?.isEmpty ?? true) { return ''; } return null; }, ); } }