basic container sugegstion implementation
This commit is contained in:
@@ -279,6 +279,7 @@ class _TabSheetHeader extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return ContainerChips(
|
return ContainerChips(
|
||||||
|
showGroupSuggestions: true,
|
||||||
selectedContainer: selectedContainer,
|
selectedContainer: selectedContainer,
|
||||||
onSelected: (container) async {
|
onSelected: (container) async {
|
||||||
if (container != null) {
|
if (container != null) {
|
||||||
|
|||||||
@@ -134,8 +134,8 @@ class GeckoInferenceRepository extends _$GeckoInferenceRepository {
|
|||||||
return neighbors;
|
return neighbors;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<List<String>>?> suggestClusters({
|
Future<List<({String? topic, List<String> tabIds})>?> suggestClusters({
|
||||||
required List<String> unassignedDocumentsInput,
|
required Map<String, String> unassignedDocumentsInput,
|
||||||
}) async {
|
}) async {
|
||||||
if (!ref.read(
|
if (!ref.read(
|
||||||
generalSettingsWithDefaultsProvider.select(
|
generalSettingsWithDefaultsProvider.select(
|
||||||
@@ -146,7 +146,9 @@ class GeckoInferenceRepository extends _$GeckoInferenceRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final processedDocuments = <String, String>{};
|
final processedDocuments = <String, String>{};
|
||||||
final unassignedDocumentsProcessed = unassignedDocumentsInput.map((doc) {
|
final unassignedDocumentsProcessed = unassignedDocumentsInput.values.map((
|
||||||
|
doc,
|
||||||
|
) {
|
||||||
final processed = preprocessText(doc);
|
final processed = preprocessText(doc);
|
||||||
if (processed != doc) {
|
if (processed != doc) {
|
||||||
processedDocuments[processed] = doc;
|
processedDocuments[processed] = doc;
|
||||||
@@ -168,9 +170,25 @@ class GeckoInferenceRepository extends _$GeckoInferenceRepository {
|
|||||||
.toList(),
|
.toList(),
|
||||||
);
|
);
|
||||||
|
|
||||||
print(clusters);
|
final clusterResult = await clusters.mapNotNull(
|
||||||
|
(cluster) => Future.wait(
|
||||||
|
cluster.map((clusterTitles) async {
|
||||||
|
final topic = await predictDocumentTopic(clusterTitles.toSet());
|
||||||
|
|
||||||
return null;
|
return (
|
||||||
|
topic: topic,
|
||||||
|
tabIds: clusterTitles.map((title) {
|
||||||
|
final originalTitle = processedDocuments[title] ?? title;
|
||||||
|
return unassignedDocumentsInput.entries
|
||||||
|
.firstWhere((entry) => entry.value == originalTitle)
|
||||||
|
.key;
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return clusterResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, List<double>>?> generateDocumentEmbeddings(
|
Future<Map<String, List<double>>?> generateDocumentEmbeddings(
|
||||||
@@ -236,26 +254,25 @@ Future<String?> containerTopic(Ref ref, String containerId) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Riverpod()
|
@Riverpod()
|
||||||
Future<List<List<String>>?> suggestClusters(Ref ref) async {
|
Future<List<({List<String> tabIds, String? topic})>?> suggestClusters(
|
||||||
|
Ref ref,
|
||||||
|
) async {
|
||||||
final unassignedTitles = await ref.watch(
|
final unassignedTitles = await ref.watch(
|
||||||
containerTabsDataProvider(null).selectAsync(
|
containerTabsDataProvider(null).selectAsync(
|
||||||
(tabData) => EquatableValue(
|
(tabData) => EquatableValue(
|
||||||
tabData
|
Map.fromEntries(
|
||||||
.where((tab) => tab.title.isNotEmpty)
|
tabData
|
||||||
.map((tab) => (tab.id, tab.title!))
|
.where((tab) => tab.title.isNotEmpty)
|
||||||
.toSet(),
|
.map((tab) => MapEntry(tab.id, tab.title!)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (unassignedTitles.value.isNotEmpty) {
|
if (unassignedTitles.value.isNotEmpty) {
|
||||||
await ref
|
return await ref
|
||||||
.read(geckoInferenceRepositoryProvider.notifier)
|
.read(geckoInferenceRepositoryProvider.notifier)
|
||||||
.suggestClusters(
|
.suggestClusters(unassignedDocumentsInput: unassignedTitles.value);
|
||||||
unassignedDocumentsInput: unassignedTitles.value
|
|
||||||
.map((tab) => tab.$2)
|
|
||||||
.toList(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
+10
-11
@@ -42,7 +42,7 @@ final class GeckoInferenceRepositoryProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$geckoInferenceRepositoryHash() =>
|
String _$geckoInferenceRepositoryHash() =>
|
||||||
r'1f08047bee912a475bfd0851f8a9868a41bca836';
|
r'eb174c9f80f3537bc075ebbffcef787ec55ad217';
|
||||||
|
|
||||||
abstract class _$GeckoInferenceRepository extends $Notifier<void> {
|
abstract class _$GeckoInferenceRepository extends $Notifier<void> {
|
||||||
void build();
|
void build();
|
||||||
@@ -138,13 +138,13 @@ const suggestClustersProvider = SuggestClustersProvider._();
|
|||||||
final class SuggestClustersProvider
|
final class SuggestClustersProvider
|
||||||
extends
|
extends
|
||||||
$FunctionalProvider<
|
$FunctionalProvider<
|
||||||
AsyncValue<List<List<String>>?>,
|
AsyncValue<List<({List<String> tabIds, String? topic})>?>,
|
||||||
List<List<String>>?,
|
List<({List<String> tabIds, String? topic})>?,
|
||||||
FutureOr<List<List<String>>?>
|
FutureOr<List<({List<String> tabIds, String? topic})>?>
|
||||||
>
|
>
|
||||||
with
|
with
|
||||||
$FutureModifier<List<List<String>>?>,
|
$FutureModifier<List<({List<String> tabIds, String? topic})>?>,
|
||||||
$FutureProvider<List<List<String>>?> {
|
$FutureProvider<List<({List<String> tabIds, String? topic})>?> {
|
||||||
const SuggestClustersProvider._()
|
const SuggestClustersProvider._()
|
||||||
: super(
|
: super(
|
||||||
from: null,
|
from: null,
|
||||||
@@ -161,17 +161,16 @@ final class SuggestClustersProvider
|
|||||||
|
|
||||||
@$internal
|
@$internal
|
||||||
@override
|
@override
|
||||||
$FutureProviderElement<List<List<String>>?> $createElement(
|
$FutureProviderElement<List<({List<String> tabIds, String? topic})>?>
|
||||||
$ProviderPointer pointer,
|
$createElement($ProviderPointer pointer) => $FutureProviderElement(pointer);
|
||||||
) => $FutureProviderElement(pointer);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
FutureOr<List<List<String>>?> create(Ref ref) {
|
FutureOr<List<({List<String> tabIds, String? topic})>?> create(Ref ref) {
|
||||||
return suggestClusters(ref);
|
return suggestClusters(ref);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$suggestClustersHash() => r'9d212158b80ba53afe305db938ab399b1e931d88';
|
String _$suggestClustersHash() => r'592058ed092384c37a185dcd5657276a3dde5b4e';
|
||||||
|
|
||||||
@ProviderFor(containerTabSuggestions)
|
@ProviderFor(containerTabSuggestions)
|
||||||
const containerTabSuggestionsProvider = ContainerTabSuggestionsFamily._();
|
const containerTabSuggestionsProvider = ContainerTabSuggestionsFamily._();
|
||||||
|
|||||||
@@ -23,16 +23,21 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:nullability/nullability.dart';
|
import 'package:nullability/nullability.dart';
|
||||||
|
import 'package:weblibre/core/logger.dart';
|
||||||
import 'package:weblibre/core/routing/routes.dart';
|
import 'package:weblibre/core/routing/routes.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/presentation/controllers/tab_suggestions.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/data/entities/container_filter.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/data/entities/container_filter.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/domain/providers.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/domain/providers.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/gecko_inference.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/container_title.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/container_title.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/tab_drag_container_target.dart';
|
import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/tab_drag_container_target.dart';
|
||||||
|
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||||
import 'package:weblibre/presentation/widgets/selectable_chips.dart';
|
import 'package:weblibre/presentation/widgets/selectable_chips.dart';
|
||||||
|
|
||||||
class ContainerChips extends HookConsumerWidget {
|
class ContainerChips extends HookConsumerWidget {
|
||||||
final bool displayMenu;
|
final bool displayMenu;
|
||||||
|
final bool showGroupSuggestions;
|
||||||
|
|
||||||
final ContainerData? selectedContainer;
|
final ContainerData? selectedContainer;
|
||||||
final bool Function(ContainerDataWithCount)? containerFilter;
|
final bool Function(ContainerDataWithCount)? containerFilter;
|
||||||
@@ -48,6 +53,7 @@ class ContainerChips extends HookConsumerWidget {
|
|||||||
this.containerFilter,
|
this.containerFilter,
|
||||||
this.searchTextListenable,
|
this.searchTextListenable,
|
||||||
this.displayMenu = true,
|
this.displayMenu = true,
|
||||||
|
this.showGroupSuggestions = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -132,6 +138,53 @@ class ContainerChips extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (showGroupSuggestions)
|
||||||
|
Consumer(
|
||||||
|
builder: (context, ref, child) {
|
||||||
|
final tabSuggestionsEnabled = ref.watch(
|
||||||
|
tabSuggestionsControllerProvider,
|
||||||
|
);
|
||||||
|
final enableAiFeatures = ref.watch(
|
||||||
|
generalSettingsWithDefaultsProvider.select(
|
||||||
|
(settings) => settings.enableLocalAiFeatures,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!enableAiFeatures || !tabSuggestionsEnabled) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
final suggestions = ref.watch(
|
||||||
|
suggestClustersProvider,
|
||||||
|
);
|
||||||
|
|
||||||
|
return suggestions.when(
|
||||||
|
data: (data) {
|
||||||
|
if (data.isEmpty) {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
return FilterChip(
|
||||||
|
avatar: const Icon(MdiIcons.autoFix),
|
||||||
|
label: Text(data!.length.toString()),
|
||||||
|
showCheckmark: false,
|
||||||
|
onSelected: (value) {},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
error: (error, stackTrace) {
|
||||||
|
logger.e(
|
||||||
|
'Error suggesting containers',
|
||||||
|
error: error,
|
||||||
|
stackTrace: stackTrace,
|
||||||
|
);
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
},
|
||||||
|
loading: () {
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
availableItems: availableContainers,
|
availableItems: availableContainers,
|
||||||
selectedItem: selectedContainer,
|
selectedItem: selectedContainer,
|
||||||
|
|||||||
Reference in New Issue
Block a user