bang and search

This commit is contained in:
Fabian Freund
2025-02-13 14:29:37 +01:00
parent fe4baa58f5
commit e09219e07c
21 changed files with 499 additions and 314 deletions
@@ -36,11 +36,25 @@ class SelectedBangTrigger extends _$SelectedBangTrigger {
}
@Riverpod()
Stream<BangData?> selectedBangData(Ref ref, {String? domain}) {
final repository = ref.watch(bangDataRepositoryProvider.notifier);
final selectedBangTrigger =
ref.watch(selectedBangTriggerProvider(domain: domain));
return repository.watchBang(selectedBangTrigger);
class SelectedBangData extends _$SelectedBangData {
@override
BangData? build({String? domain}) {
final repository = ref.watch(bangDataRepositoryProvider.notifier);
final selectedBangTrigger =
ref.watch(selectedBangTriggerProvider(domain: domain));
final subscription = repository.watchBang(selectedBangTrigger).listen(
(value) {
state = value;
},
);
ref.onDispose(() {
unawaited(subscription.cancel());
});
return null;
}
}
@Riverpod(keepAlive: true)
@@ -6,7 +6,7 @@ part of 'providers.dart';
// RiverpodGenerator
// **************************************************************************
String _$selectedBangDataHash() => r'd43e679c40d35c4f4c692ee286dc21789d7287dd';
String _$availableTabIdsHash() => r'd8b55b012b38111c8d5e069987a55645d1a71b89';
/// Copied from Dart SDK
class _SystemHash {
@@ -29,137 +29,6 @@ class _SystemHash {
}
}
/// See also [selectedBangData].
@ProviderFor(selectedBangData)
const selectedBangDataProvider = SelectedBangDataFamily();
/// See also [selectedBangData].
class SelectedBangDataFamily extends Family<AsyncValue<BangData?>> {
/// See also [selectedBangData].
const SelectedBangDataFamily();
/// See also [selectedBangData].
SelectedBangDataProvider call({
String? domain,
}) {
return SelectedBangDataProvider(
domain: domain,
);
}
@override
SelectedBangDataProvider getProviderOverride(
covariant SelectedBangDataProvider provider,
) {
return call(
domain: provider.domain,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'selectedBangDataProvider';
}
/// See also [selectedBangData].
class SelectedBangDataProvider extends AutoDisposeStreamProvider<BangData?> {
/// See also [selectedBangData].
SelectedBangDataProvider({
String? domain,
}) : this._internal(
(ref) => selectedBangData(
ref as SelectedBangDataRef,
domain: domain,
),
from: selectedBangDataProvider,
name: r'selectedBangDataProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$selectedBangDataHash,
dependencies: SelectedBangDataFamily._dependencies,
allTransitiveDependencies:
SelectedBangDataFamily._allTransitiveDependencies,
domain: domain,
);
SelectedBangDataProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.domain,
}) : super.internal();
final String? domain;
@override
Override overrideWith(
Stream<BangData?> Function(SelectedBangDataRef provider) create,
) {
return ProviderOverride(
origin: this,
override: SelectedBangDataProvider._internal(
(ref) => create(ref as SelectedBangDataRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
domain: domain,
),
);
}
@override
AutoDisposeStreamProviderElement<BangData?> createElement() {
return _SelectedBangDataProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is SelectedBangDataProvider && other.domain == domain;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, domain.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin SelectedBangDataRef on AutoDisposeStreamProviderRef<BangData?> {
/// The parameter `domain` of this provider.
String? get domain;
}
class _SelectedBangDataProviderElement
extends AutoDisposeStreamProviderElement<BangData?>
with SelectedBangDataRef {
_SelectedBangDataProviderElement(super.provider);
@override
String? get domain => (origin as SelectedBangDataProvider).domain;
}
String _$availableTabIdsHash() => r'd8b55b012b38111c8d5e069987a55645d1a71b89';
/// See also [availableTabIds].
@ProviderFor(availableTabIds)
const availableTabIdsProvider = AvailableTabIdsFamily();
@@ -893,6 +762,152 @@ class _SelectedBangTriggerProviderElement
String? get domain => (origin as SelectedBangTriggerProvider).domain;
}
String _$selectedBangDataHash() => r'28c394e5989e108870ca5733e916b80b3cf8972c';
abstract class _$SelectedBangData
extends BuildlessAutoDisposeNotifier<BangData?> {
late final String? domain;
BangData? build({
String? domain,
});
}
/// See also [SelectedBangData].
@ProviderFor(SelectedBangData)
const selectedBangDataProvider = SelectedBangDataFamily();
/// See also [SelectedBangData].
class SelectedBangDataFamily extends Family<BangData?> {
/// See also [SelectedBangData].
const SelectedBangDataFamily();
/// See also [SelectedBangData].
SelectedBangDataProvider call({
String? domain,
}) {
return SelectedBangDataProvider(
domain: domain,
);
}
@override
SelectedBangDataProvider getProviderOverride(
covariant SelectedBangDataProvider provider,
) {
return call(
domain: provider.domain,
);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'selectedBangDataProvider';
}
/// See also [SelectedBangData].
class SelectedBangDataProvider
extends AutoDisposeNotifierProviderImpl<SelectedBangData, BangData?> {
/// See also [SelectedBangData].
SelectedBangDataProvider({
String? domain,
}) : this._internal(
() => SelectedBangData()..domain = domain,
from: selectedBangDataProvider,
name: r'selectedBangDataProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$selectedBangDataHash,
dependencies: SelectedBangDataFamily._dependencies,
allTransitiveDependencies:
SelectedBangDataFamily._allTransitiveDependencies,
domain: domain,
);
SelectedBangDataProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.domain,
}) : super.internal();
final String? domain;
@override
BangData? runNotifierBuild(
covariant SelectedBangData notifier,
) {
return notifier.build(
domain: domain,
);
}
@override
Override overrideWith(SelectedBangData Function() create) {
return ProviderOverride(
origin: this,
override: SelectedBangDataProvider._internal(
() => create()..domain = domain,
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
domain: domain,
),
);
}
@override
AutoDisposeNotifierProviderElement<SelectedBangData, BangData?>
createElement() {
return _SelectedBangDataProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is SelectedBangDataProvider && other.domain == domain;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, domain.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin SelectedBangDataRef on AutoDisposeNotifierProviderRef<BangData?> {
/// The parameter `domain` of this provider.
String? get domain;
}
class _SelectedBangDataProviderElement
extends AutoDisposeNotifierProviderElement<SelectedBangData, BangData?>
with SelectedBangDataRef {
_SelectedBangDataProviderElement(super.provider);
@override
String? get domain => (origin as SelectedBangDataProvider).domain;
}
String _$lastUsedAssistantModeHash() =>
r'1255a2754a3ea5ea058d110fd9743897fe103a69';
@@ -19,13 +19,17 @@ import 'package:lensai/features/geckoview/domain/providers/tab_session.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_state.dart';
import 'package:lensai/features/geckoview/domain/repositories/tab.dart';
import 'package:lensai/features/geckoview/features/browser/domain/entities/sheet.dart';
import 'package:lensai/features/geckoview/features/search/domain/providers/engine_suggestions.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/share_intent/domain/entities/shared_content.dart';
import 'package:lensai/features/user/domain/providers.dart';
import 'package:lensai/presentation/hooks/listenable_callback.dart';
import 'package:lensai/presentation/widgets/auto_suggest_text_field.dart';
import 'package:lensai/presentation/widgets/failure_widget.dart';
import 'package:lensai/presentation/widgets/website_title_tile.dart';
import 'package:lensai/utils/ui_helper.dart' as ui_helper;
import 'package:lensai/utils/uri_parser.dart' as uri_parser;
import 'package:share_plus/share_plus.dart';
class WebPageDialog extends HookConsumerWidget {
@@ -52,12 +56,8 @@ class WebPageDialog extends HookConsumerWidget {
),
),
);
final availableBangCount = availableBangsAsync.valueOrNull?.length;
final formKey = useMemoized(() => GlobalKey<FormState>());
final addressTextController =
useTextEditingController(text: url.toString());
final addressTextFocusNode = useFocusNode();
final availableBangCount = availableBangsAsync.valueOrNull?.length;
return MediaQuery.removeViewInsets(
context: context,
@@ -77,42 +77,73 @@ class WebPageDialog extends HookConsumerWidget {
width: double.maxFinite,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Form(
key: formKey,
child: TextFormField(
controller: addressTextController,
focusNode: addressTextFocusNode,
enableIMEPersonalizedLearning: !incognitoEnabled,
keyboardType: TextInputType.url,
decoration: const InputDecoration(
labelText: 'Address',
),
validator: (value) {
if (value != null && Uri.tryParse(value) != null) {
return null;
}
child: HookConsumer(
builder: (context, ref, child) {
final addressTextController =
useTextEditingController(text: url.toString());
final addressTextFocusNode = useFocusNode();
return 'Invalid Address';
},
onTap: () {
if (!addressTextFocusNode.hasFocus) {
// Select all text when the field is tapped
addressTextController.selection = TextSelection(
baseOffset: 0,
extentOffset: addressTextController.text.length,
);
}
},
onFieldSubmitted: (value) async {
await ref
.read(tabSessionProvider(tabId: null).notifier)
.loadUrl(url: Uri.tryParse(value)!);
final suggestion = useState<String?>(null);
if (context.mounted) {
context.pop();
}
},
),
useListenableCallback(
addressTextController,
() async {
if (addressTextController.text.isNotEmpty) {
final result = await ref
.read(engineSuggestionsProvider.notifier)
.getAutocompleteSuggestion(
addressTextController.text,
);
suggestion.value = result;
}
},
);
return AutoSuggestTextField(
controller: addressTextController,
focusNode: addressTextFocusNode,
suggestion: suggestion.value,
enableIMEPersonalizedLearning: !incognitoEnabled,
keyboardType: TextInputType.url,
decoration: const InputDecoration(
label: Text('Address'),
floatingLabelBehavior: FloatingLabelBehavior.always,
),
onTap: () {
if (!addressTextFocusNode.hasFocus) {
// Select all text when the field is tapped
addressTextController.selection = TextSelection(
baseOffset: 0,
extentOffset: addressTextController.text.length,
);
}
},
onSubmitted: (value) async {
if (value.isNotEmpty) {
var newUrl =
uri_parser.tryParseUrl(value, eagerParsing: true);
if (newUrl == null) {
final defaultSearchBang = await ref
.read(defaultSearchBangDataProvider.future);
newUrl = defaultSearchBang?.getUrl(value);
}
if (newUrl != null) {
await ref
.read(tabSessionProvider(tabId: null).notifier)
.loadUrl(url: newUrl);
if (context.mounted) {
context.pop();
}
}
}
},
);
},
),
),
),
@@ -16,10 +16,10 @@ import 'package:lensai/features/geckoview/domain/providers/tab_state.dart';
import 'package:lensai/features/geckoview/domain/providers/web_extensions_state.dart';
import 'package:lensai/features/geckoview/domain/repositories/tab.dart';
import 'package:lensai/features/geckoview/features/browser/domain/entities/sheet.dart';
import 'package:lensai/features/geckoview/features/browser/domain/services/create_tab.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/app_bar_title.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/browser_view.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/draggable_scrollable_header.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/edit_url_dialog.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/extension_badge_icon.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/sheets/create_tab.dart';
import 'package:lensai/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart';
@@ -125,57 +125,74 @@ class BrowserScreen extends HookConsumerWidget {
extra: tabState,
);
},
onDoubleTap: () async {
final newUrl = await showDialog<Uri?>(
context: context,
builder: (context) => EditUrlDialog(
initialUrl: tabState.url,
),
);
if (newUrl != null) {
await ref
.read(
tabSessionProvider(tabId: null)
.notifier,
)
.loadUrl(url: newUrl);
}
},
)
: const SizedBox.shrink();
},
)
: HookBuilder(
builder: (context) {
final activeTool = useListenableSelector(
activeKagiTool,
() {
if (displayedSheet is CreateTabSheetWidget) {
return null;
}
// final activeTool = useListenableSelector(
// activeKagiTool,
// () {
// if (displayedSheet is CreateTabSheetWidget) {
// return null;
// }
return activeKagiTool.value;
},
);
// return activeKagiTool.value;
// },
// );
return Row(
children: [
IconButton(
color: (activeTool == KagiTool.search)
? Theme.of(context).colorScheme.primary
: null,
onPressed: () async {
// ref
// .read(createTabStreamProvider.notifier)
// .createTab(
// CreateTabSheet(
// preferredTool: KagiTool.search,
// ),
// );
await context
.push(const SearchRoute().location);
},
icon: Icon(KagiTool.search.icon),
),
IconButton(
color: (activeTool == KagiTool.summarizer)
? Theme.of(context).colorScheme.primary
: null,
onPressed: () {
ref
.read(createTabStreamProvider.notifier)
.createTab(
CreateTabSheet(
preferredTool: KagiTool.summarizer,
),
);
},
icon: Icon(KagiTool.summarizer.icon),
),
// IconButton(
// color: (activeTool == KagiTool.search)
// ? Theme.of(context).colorScheme.primary
// : null,
// onPressed: () async {
// // ref
// // .read(createTabStreamProvider.notifier)
// // .createTab(
// // CreateTabSheet(
// // preferredTool: KagiTool.search,
// // ),
// // );
// await context
// .push(const SearchRoute().location);
// },
// icon: Icon(KagiTool.search.icon),
// ),
// IconButton(
// color: (activeTool == KagiTool.summarizer)
// ? Theme.of(context).colorScheme.primary
// : null,
// onPressed: () {
// ref
// .read(createTabStreamProvider.notifier)
// .createTab(
// CreateTabSheet(
// preferredTool: KagiTool.summarizer,
// ),
// );
// },
// icon: Icon(KagiTool.summarizer.icon),
// ),
// if (showEarlyAccessFeatures)
// IconButton(
// color: (activeTool == KagiTool.assistant)
@@ -199,7 +216,9 @@ class BrowserScreen extends HookConsumerWidget {
},
),
actions: [
if (selectedTabId != null) ReaderButton(),
if (selectedTabId != null &&
displayedSheet is! ViewTabsSheet)
ReaderButton(),
// if (quickAction != null)
// InkWell(
// onTap: () async {
@@ -268,9 +287,7 @@ class BrowserScreen extends HookConsumerWidget {
),
MenuItemButton(
onPressed: () async {
await ref
.read(tabRepositoryProvider.notifier)
.addTab(url: Uri.https('kagi.com'));
await context.push(const SearchRoute().location);
},
leadingIcon: const Icon(Icons.add),
child: const Text('Add Tab'),
@@ -586,14 +603,6 @@ class BrowserScreen extends HookConsumerWidget {
leadingIcon: const Icon(Icons.info),
child: const Text('Auth'),
),
MenuItemButton(
onPressed: () async {
await context
.push(WebEngineHardeningRoute().location);
},
leadingIcon: const Icon(Icons.info),
child: const Text('Pref'),
),
],
),
],
@@ -8,8 +8,14 @@ class AppBarTitle extends StatelessWidget {
final TabState tab;
final void Function()? onTap;
final void Function()? onDoubleTap;
const AppBarTitle({required this.tab, this.onTap, super.key});
const AppBarTitle({
required this.tab,
this.onTap,
this.onDoubleTap,
super.key,
});
Icon _securityStatusIcon(BuildContext context) {
if (tab.url.isScheme('http')) {
@@ -42,6 +48,7 @@ class AppBarTitle extends StatelessWidget {
final theme = Theme.of(context);
return GestureDetector(
onTap: onTap,
onDoubleTap: onDoubleTap,
child: Row(
children: [
Skeletonizer(
@@ -5,6 +5,7 @@ import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/core/logger.dart';
import 'package:lensai/features/geckoview/domain/providers.dart';
import 'package:lensai/features/geckoview/domain/providers/selected_tab.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_state.dart';
import 'package:lensai/features/geckoview/domain/providers/web_extensions_state.dart';
import 'package:lensai/features/geckoview/domain/repositories/tab.dart';
@@ -49,6 +50,9 @@ class _BrowserViewState extends ConsumerState<BrowserView>
@override
Widget build(BuildContext context) {
final hasTab =
ref.watch(selectedTabProvider.select((value) => value != null));
ref.listen(
selectedTabStateProvider.select(
(state) => (tabId: state?.id, isLoading: state?.isLoading),
@@ -69,34 +73,42 @@ class _BrowserViewState extends ConsumerState<BrowserView>
},
);
return GeckoView(
preInitializationStep: () async {
await ref
.read(generalSettingsRepositoryProvider.notifier)
.fetch()
.then((settings) {
ref
.read(deleteBrowserDataServiceProvider.notifier)
.deleteData(settings.deleteBrowsingDataOnQuit);
});
return Visibility(
visible: hasTab,
replacement: SizedBox.expand(
child: Container(
color: Colors.grey[800],
),
),
child: GeckoView(
preInitializationStep: () async {
await ref
.read(generalSettingsRepositoryProvider.notifier)
.fetch()
.then((settings) {
ref
.read(deleteBrowserDataServiceProvider.notifier)
.deleteData(settings.deleteBrowsingDataOnQuit);
});
await ref
.read(eventServiceProvider)
.viewReadyStateEvents
.firstWhere((state) => state == true)
.timeout(
const Duration(seconds: 3),
onTimeout: () {
logger.e(
'Browser fragement not reported ready, trying to intitialize anyways',
);
return true;
},
);
},
postInitializationStep: () async {
await widget.postInitializationStep?.call();
},
await ref
.read(eventServiceProvider)
.viewReadyStateEvents
.firstWhere((state) => state == true)
.timeout(
const Duration(seconds: 3),
onTimeout: () {
logger.e(
'Browser fragement not reported ready, trying to intitialize anyways',
);
return true;
},
);
},
postInitializationStep: () async {
await widget.postInitializationStep?.call();
},
),
);
}
@@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:lensai/utils/uri_parser.dart' as uri_parser;
class EditUrlDialog extends HookWidget {
final Uri initialUrl;
const EditUrlDialog({required this.initialUrl});
@override
Widget build(BuildContext context) {
final formKey = useMemoized(() => GlobalKey<FormState>());
final urlController = useTextEditingController(text: initialUrl.toString());
return AlertDialog(
title: const Text('Edit Address'),
content: Form(
key: formKey,
child: TextFormField(
controller: urlController,
maxLines: null,
keyboardType: TextInputType.url,
decoration: const InputDecoration(
hintText: 'Enter URL',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a URL';
}
if (uri_parser.tryParseUrl(value) == null) {
return 'Please enter a valid URL';
}
return null;
},
),
),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cancel'),
),
TextButton(
onPressed: () {
if (formKey.currentState?.validate() ?? false) {
Navigator.of(context).pop(Uri.parse(urlController.text));
}
},
child: const Text('Edit'),
),
],
);
}
}
@@ -4,8 +4,10 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_reorderable_grid_view/widgets/widgets.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/core/providers.dart';
import 'package:lensai/core/routing/routes.dart';
import 'package:lensai/data/models/drag_data.dart';
import 'package:lensai/features/geckoview/domain/providers/selected_tab.dart';
import 'package:lensai/features/geckoview/domain/providers/tab_state.dart';
@@ -428,10 +430,7 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
),
child: FloatingActionButton.small(
onPressed: () async {
await ref.read(tabRepositoryProvider.notifier).addTab(
url: Uri.https('setcookie.net'),
// contextId: uuid.v4(),
);
await context.push(const SearchRoute().location);
onClose();
},
@@ -12,6 +12,7 @@ import 'package:lensai/features/geckoview/features/search/presentation/widgets/s
import 'package:lensai/features/geckoview/features/search/presentation/widgets/search_modules/history_suggestions.dart';
import 'package:lensai/features/geckoview/features/search/presentation/widgets/search_modules/search_suggestions.dart';
import 'package:lensai/features/geckoview/features/search/presentation/widgets/search_modules/tab_search.dart';
import 'package:lensai/utils/uri_parser.dart' as uri_parser;
class SearchScreen extends HookConsumerWidget {
final String? initialSearchText;
@@ -37,13 +38,11 @@ class SearchScreen extends HookConsumerWidget {
ref.listen(
selectedBangDataProvider(),
(previous, next) {
if (next.hasValue) {
if ((previous?.hasValue ?? false) && previous!.value != next.value) {
showBangIcon.value = true;
}
selectedBang.value = next.value;
if (previous != next) {
showBangIcon.value = true;
}
selectedBang.value = next;
},
);
@@ -77,18 +76,31 @@ class SearchScreen extends HookConsumerWidget {
textEditingController: searchTextController,
focusNode: searchFocusNode,
autofocus: true,
onSubmitted: (text) async {
if (Uri.tryParse(text) case final Uri url) {
await ref
.read(tabRepositoryProvider.notifier)
.addTab(url: url);
onSubmitted: (value) async {
if (value.isNotEmpty) {
var newUrl =
uri_parser.tryParseUrl(value, eagerParsing: true);
if (context.mounted) {
ref
.read(bottomSheetControllerProvider.notifier)
.dismiss();
if (newUrl == null) {
final defaultSearchBang = ref
.read(selectedBangDataProvider()) ??
await ref.read(defaultSearchBangDataProvider.future);
context.pop();
newUrl = defaultSearchBang?.getUrl(value);
}
if (newUrl != null) {
await ref
.read(tabRepositoryProvider.notifier)
.addTab(url: newUrl);
if (context.mounted) {
ref
.read(bottomSheetControllerProvider.notifier)
.dismiss();
context.pop();
}
}
}
},
@@ -12,6 +12,7 @@ import 'package:lensai/presentation/widgets/speech_to_text_button.dart';
class SearchField extends HookConsumerWidget {
final TextEditingController textEditingController;
final FocusNode? focusNode;
final Widget? label;
final bool autofocus;
final void Function(String)? onSubmitted;
final bool showSuggestions;
@@ -25,6 +26,7 @@ class SearchField extends HookConsumerWidget {
required this.onSubmitted,
required this.activeBang,
required this.showSuggestions,
this.label,
this.focusNode,
this.showBangIcon = true,
this.autofocus = false,
@@ -70,7 +72,7 @@ class SearchField extends HookConsumerWidget {
child: UrlIcon(activeBang!.getUrl(''), iconSize: 24.0),
)
: null,
label: const Text('Search'),
label: label ?? const Text('Search'),
// hintText: 'Ask anything...',
floatingLabelBehavior: FloatingLabelBehavior.always,
suffixIcon: hasText
@@ -81,8 +81,8 @@ class HistorySuggestions extends HookConsumerWidget {
subtitle: suggestion.description.mapNotNull(
(description) => Text(
description,
softWrap: false,
overflow: TextOverflow.fade,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
onTap: () async {
@@ -118,20 +118,30 @@ class TabSearch extends HookConsumerWidget {
),
),
),
subtitle: MarkdownBody(
data: (bodyHasMatch && !urlHasMatch)
? result.content!
: result.highlightedUrl ?? result.url.toString(),
styleSheet: MarkdownStyleSheet(
p: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
subtitle: (bodyHasMatch || urlHasMatch)
? MarkdownBody(
data: bodyHasMatch
? result.content!
: result.highlightedUrl!,
styleSheet: MarkdownStyleSheet(
p: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context)
.colorScheme
.onSurfaceVariant,
),
a: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context)
.colorScheme
.onSurfaceVariant,
decoration: TextDecoration.none,
),
),
a: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
decoration: TextDecoration.none,
),
),
),
)
: Text(
result.url.toString(),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
onTap: () async {
await ref
.read(tabRepositoryProvider.notifier)
@@ -30,7 +30,7 @@ class QAMemoryChain extends _$QAMemoryChain {
contextId: contextId,
).asRetriever(
defaultOptions: const VectorStoreRetrieverOptions(
searchType: VectorStoreSimilaritySearch(k: 3),
searchType: VectorStoreSimilaritySearch(k: 6),
),
);
@@ -6,7 +6,7 @@ part of 'qa_memory_chain.dart';
// RiverpodGenerator
// **************************************************************************
String _$qAMemoryChainHash() => r'd5efc3fc01a6a88c67164d7eefc06412c9ab7c5c';
String _$qAMemoryChainHash() => r'f05ff182416cd8ac8c35f046081652f8964684a8';
/// Copied from Dart SDK
class _SystemHash {