diff --git a/frontend/appflowy_flutter/lib/workspace/application/command_palette/command_palette_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/command_palette/command_palette_bloc.dart index b53f3f8c23..01f638fe7a 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/command_palette/command_palette_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/command_palette/command_palette_bloc.dart @@ -94,15 +94,16 @@ class CommandPaletteBloc emit( state.copyWith( query: null, - isLoading: false, + searching: false, serverResponseItems: [], localResponseItems: [], combinedResponseItems: {}, resultSummaries: [], + generatingAIOverview: false, ), ); } else { - emit(state.copyWith(query: event.search, isLoading: true)); + emit(state.copyWith(query: event.search, searching: true)); _activeQuery = event.search; unawaited( @@ -122,7 +123,8 @@ class CommandPaletteBloc add( CommandPaletteEvent.resultsChanged( searchId: '', - isLoading: false, + searching: false, + generatingAIOverview: false, ), ); } @@ -150,19 +152,23 @@ class CommandPaletteBloc searchId: searchId, localItems: items, ), - onServerItems: (items, searchId, isLoading) => _handleResultsUpdate( + onServerItems: (items, searchId, searching, generatingAIOverview) => + _handleResultsUpdate( searchId: searchId, serverItems: items, - isLoading: isLoading, + searching: searching, + generatingAIOverview: generatingAIOverview, ), - onSummaries: (summaries, searchId, isLoading) => _handleResultsUpdate( + onSummaries: (summaries, searchId, searching, generatingAIOverview) => + _handleResultsUpdate( searchId: searchId, summaries: summaries, - isLoading: isLoading, + searching: searching, + generatingAIOverview: generatingAIOverview, ), onFinished: (searchId) => _handleResultsUpdate( searchId: searchId, - isLoading: false, + searching: false, ), ); } @@ -172,7 +178,8 @@ class CommandPaletteBloc List? serverItems, List? localItems, List? summaries, - bool isLoading = true, + bool searching = true, + bool generatingAIOverview = false, }) { if (_isActiveSearch(searchId)) { add( @@ -181,7 +188,8 @@ class CommandPaletteBloc serverItems: serverItems, localItems: localItems, summaries: summaries, - isLoading: isLoading, + searching: searching, + generatingAIOverview: generatingAIOverview, ), ); } @@ -223,7 +231,8 @@ class CommandPaletteBloc localResponseItems: event.localItems ?? state.localResponseItems, resultSummaries: event.summaries ?? state.resultSummaries, combinedResponseItems: combinedItems, - isLoading: event.isLoading, + searching: event.searching, + generatingAIOverview: event.generatingAIOverview, ), ); } @@ -256,7 +265,8 @@ class CommandPaletteBloc localResponseItems: [], combinedResponseItems: {}, resultSummaries: [], - isLoading: false, + searching: false, + generatingAIOverview: false, ), ); } @@ -283,7 +293,8 @@ class CommandPaletteEvent with _$CommandPaletteEvent { }) = _NewSearchStream; const factory CommandPaletteEvent.resultsChanged({ required String searchId, - required bool isLoading, + required bool searching, + required bool generatingAIOverview, List? serverItems, List? localItems, List? summaries, @@ -324,12 +335,14 @@ class CommandPaletteState with _$CommandPaletteState { @Default({}) Map combinedResponseItems, @Default([]) List resultSummaries, @Default(null) SearchResponseStream? searchResponseStream, - required bool isLoading, + required bool searching, + required bool generatingAIOverview, @Default([]) List trash, @Default(null) String? searchId, }) = _CommandPaletteState; factory CommandPaletteState.initial() => const CommandPaletteState( - isLoading: false, + searching: false, + generatingAIOverview: false, ); } diff --git a/frontend/appflowy_flutter/lib/workspace/application/command_palette/search_service.dart b/frontend/appflowy_flutter/lib/workspace/application/command_palette/search_service.dart index 6f05b88081..89e5b604f8 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/command_palette/search_service.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/command_palette/search_service.dart @@ -49,12 +49,14 @@ class SearchResponseStream { void Function( List items, String searchId, - bool isLoading, + bool searching, + bool generatingAIOverview, )? _onServerItems; void Function( List summaries, String searchId, - bool isLoading, + bool searching, + bool generatingAIOverview, )? _onSummaries; void Function( @@ -78,14 +80,16 @@ class SearchResponseStream { _onServerItems?.call( searchState.response.searchResult.items, searchId, - searchState.isLoading, + searchState.response.searching, + searchState.response.generatingAiSummary, ); } if (searchState.response.hasSearchSummary()) { _onSummaries?.call( searchState.response.searchSummary.items, searchId, - searchState.isLoading, + searchState.response.searching, + searchState.response.generatingAiSummary, ); } @@ -105,11 +109,13 @@ class SearchResponseStream { List items, String searchId, bool isLoading, + bool generatingAIOverview, )? onServerItems, required void Function( List summaries, String searchId, bool isLoading, + bool generatingAIOverview, )? onSummaries, required void Function( List items, diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/command_palette.dart b/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/command_palette.dart index a08c46679c..648712bd15 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/command_palette.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/command_palette.dart @@ -144,7 +144,7 @@ class CommandPaletteModal extends StatelessWidget { // Change mainAxisSize to max so Expanded works correctly. Column( children: [ - SearchField(query: state.query, isLoading: state.isLoading), + SearchField(query: state.query, isLoading: state.searching), if (state.query?.isEmpty ?? true) ...[ const Divider(height: 0), Flexible( @@ -167,7 +167,7 @@ class CommandPaletteModal extends StatelessWidget { // When there are no results and the query is not empty and not loading, // show the no results message, centered in the available space. else if ((state.query?.isNotEmpty ?? false) && - !state.isLoading) ...[ + !state.searching) ...[ const Divider(height: 0), Expanded( child: const _NoResultsHint(), diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/widgets/search_result_cell.dart b/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/widgets/search_result_cell.dart index 7034b79821..e8e5a37f82 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/widgets/search_result_cell.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/widgets/search_result_cell.dart @@ -207,9 +207,12 @@ class SearchResultPreview extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - FlowyText(LocaleKeys.commandPalette_pagePreview.tr()), - const Divider( - thickness: 1, + Opacity( + opacity: 0.5, + child: FlowyText( + LocaleKeys.commandPalette_pagePreview.tr(), + fontSize: 12, + ), ), const VSpace(6), Expanded( diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/widgets/search_results_list.dart b/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/widgets/search_results_list.dart index af2944d09c..483da06f40 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/widgets/search_results_list.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/widgets/search_results_list.dart @@ -3,6 +3,7 @@ import 'package:appflowy/workspace/application/action_navigation/action_navigati import 'package:appflowy/workspace/application/action_navigation/navigation_action.dart'; import 'package:appflowy/workspace/application/command_palette/command_palette_bloc.dart'; import 'package:appflowy/workspace/application/command_palette/search_result_list_bloc.dart'; +import 'package:appflowy_backend/log.dart'; import 'package:flutter/material.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; @@ -11,6 +12,7 @@ import 'package:appflowy_backend/protobuf/flowy-search/result.pb.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_animate/flutter_animate.dart'; import 'search_result_cell.dart'; import 'search_summary_cell.dart'; @@ -35,22 +37,37 @@ class SearchResultList extends StatelessWidget { ), ); - Widget _buildSummariesSection() { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildSectionHeader(LocaleKeys.commandPalette_aiOverview.tr()), - ListView.separated( - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: resultSummaries.length, - separatorBuilder: (_, __) => const Divider(height: 0), - itemBuilder: (_, index) => SearchSummaryCell( - summary: resultSummaries[index], + Widget _buildAIOverviewSection(BuildContext context) { + final state = context.read().state; + + if (state.generatingAIOverview) { + return Row( + children: [ + _buildSectionHeader(LocaleKeys.commandPalette_aiOverview.tr()), + const HSpace(10), + const AIOverviewIndicator(), + ], + ); + } + if (resultSummaries.isNotEmpty) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildSectionHeader(LocaleKeys.commandPalette_aiOverview.tr()), + ListView.separated( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: resultSummaries.length, + separatorBuilder: (_, __) => const Divider(height: 0), + itemBuilder: (_, index) => SearchSummaryCell( + summary: resultSummaries[index], + ), ), - ), - ], - ); + ], + ); + } + + return const SizedBox.shrink(); } Widget _buildResultsSection(BuildContext context) { @@ -83,6 +100,8 @@ class SearchResultList extends StatelessWidget { child: BlocProvider( create: (context) => SearchResultListBloc(), child: BlocListener( + listenWhen: (previous, current) => + previous.openPageId != current.openPageId, listener: (context, state) { if (state.openPageId != null) { FlowyOverlay.pop(context); @@ -102,24 +121,28 @@ class SearchResultList extends StatelessWidget { shrinkWrap: true, physics: const ClampingScrollPhysics(), children: [ - if (resultSummaries.isNotEmpty) _buildSummariesSection(), + _buildAIOverviewSection(context), const VSpace(10), if (resultItems.isNotEmpty) _buildResultsSection(context), ], ), ), const HSpace(10), - if (resultItems.any((item) => item.content.isNotEmpty)) + if (resultItems.any((item) => item.content.isNotEmpty)) ...[ + const VerticalDivider( + thickness: 1.0, + ), Flexible( flex: 3, child: Padding( padding: const EdgeInsets.symmetric( - horizontal: 12, + horizontal: 8, vertical: 16, ), child: const SearchCellPreview(), ), ), + ], ], ), ), @@ -145,3 +168,69 @@ class SearchCellPreview extends StatelessWidget { ); } } + +class AIOverviewIndicator extends StatelessWidget { + const AIOverviewIndicator({ + super.key, + this.duration = const Duration(seconds: 1), + }); + + final Duration duration; + + @override + Widget build(BuildContext context) { + final slice = Duration(milliseconds: duration.inMilliseconds ~/ 5); + return SelectionContainer.disabled( + child: SizedBox( + height: 20, + width: 100, + child: SeparatedRow( + separatorBuilder: () => const HSpace(4), + children: [ + buildDot(const Color(0xFF9327FF)) + .animate(onPlay: (controller) => controller.repeat()) + .slideY(duration: slice, begin: 0, end: -1) + .then() + .slideY(begin: -1, end: 1) + .then() + .slideY(begin: 1, end: 0) + .then() + .slideY(duration: slice * 2, begin: 0, end: 0), + buildDot(const Color(0xFFFB006D)) + .animate(onPlay: (controller) => controller.repeat()) + .slideY(duration: slice, begin: 0, end: 0) + .then() + .slideY(begin: 0, end: -1) + .then() + .slideY(begin: -1, end: 1) + .then() + .slideY(begin: 1, end: 0) + .then() + .slideY(begin: 0, end: 0), + buildDot(const Color(0xFFFFCE00)) + .animate(onPlay: (controller) => controller.repeat()) + .slideY(duration: slice * 2, begin: 0, end: 0) + .then() + .slideY(duration: slice, begin: 0, end: -1) + .then() + .slideY(begin: -1, end: 1) + .then() + .slideY(begin: 1, end: 0), + ], + ), + ), + ); + } + + Widget buildDot(Color color) { + return SizedBox.square( + dimension: 4, + child: DecoratedBox( + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(2), + ), + ), + ); + } +} diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/widgets/search_summary_cell.dart b/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/widgets/search_summary_cell.dart index 15e8bb18f7..89553709fc 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/widgets/search_summary_cell.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/command_palette/widgets/search_summary_cell.dart @@ -1,4 +1,5 @@ import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/ai_chat/presentation/message/ai_markdown_text.dart'; import 'package:appflowy/workspace/application/command_palette/search_result_ext.dart'; import 'package:appflowy/workspace/application/command_palette/search_result_list_bloc.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -56,17 +57,50 @@ class SearchSummaryPreview extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - FlowyText(LocaleKeys.commandPalette_aiOverviewSource.tr()), - const Divider( - thickness: 1, + if (summary.highlights.isNotEmpty) ...[ + Opacity( + opacity: 0.5, + child: FlowyText( + LocaleKeys.commandPalette_aiOverviewHighlights.tr(), + fontSize: 12, + ), + ), + const VSpace(6), + SearchSummaryHighlight(text: summary.highlights), + const VSpace(36), + ], + + Opacity( + opacity: 0.5, + child: FlowyText( + LocaleKeys.commandPalette_aiOverviewSource.tr(), + fontSize: 12, + ), ), + // Sources const VSpace(6), ...summary.sources.map((e) => SearchSummarySource(source: e)), + + // Highlights ], ); } } +class SearchSummaryHighlight extends StatelessWidget { + const SearchSummaryHighlight({ + required this.text, + super.key, + }); + + final String text; + + @override + Widget build(BuildContext context) { + return AIMarkdownText(markdown: text); + } +} + class SearchSummarySource extends StatelessWidget { const SearchSummarySource({ required this.source, @@ -78,18 +112,21 @@ class SearchSummarySource extends StatelessWidget { @override Widget build(BuildContext context) { final icon = source.icon.getIcon(); - return SizedBox( - height: 30, - child: FlowyButton( - leftIcon: icon, - hoverColor: - Theme.of(context).colorScheme.primary.withValues(alpha: 0.1), - text: FlowyText(source.displayName), - onTap: () { - context.read().add( - SearchResultListEvent.openPage(pageId: source.id), - ); - }, + return FlowyTooltip( + message: LocaleKeys.commandPalette_clickToOpenPage.tr(), + child: SizedBox( + height: 30, + child: FlowyButton( + leftIcon: icon, + hoverColor: + Theme.of(context).colorScheme.primary.withValues(alpha: 0.1), + text: FlowyText(source.displayName), + onTap: () { + context.read().add( + SearchResultListEvent.openPage(pageId: source.id), + ); + }, + ), ), ); } diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index b176a08f9f..f9ddbdf012 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -2699,7 +2699,9 @@ "bestMatches": "Best matches", "aiOverview": "AI overview", "aiOverviewSource": "Reference sources", + "aiOverviewHighlights": "Highlights", "pagePreview": "Content preview", + "clickToOpenPage": "Click to open page", "recentHistory": "Recent history", "navigateHint": "to navigate", "loadingTooltip": "We are looking for results...", diff --git a/frontend/rust-lib/Cargo.lock b/frontend/rust-lib/Cargo.lock index 671d8bccf2..44d659ed56 100644 --- a/frontend/rust-lib/Cargo.lock +++ b/frontend/rust-lib/Cargo.lock @@ -493,7 +493,7 @@ checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" [[package]] name = "app-error" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=873415478ed58686c98df578e2c39d07ddce6773#873415478ed58686c98df578e2c39d07ddce6773" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a11b94240946fa8f0549e5cf1c6505b7fa7e0a16#a11b94240946fa8f0549e5cf1c6505b7fa7e0a16" dependencies = [ "anyhow", "bincode", @@ -513,7 +513,7 @@ dependencies = [ [[package]] name = "appflowy-ai-client" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=873415478ed58686c98df578e2c39d07ddce6773#873415478ed58686c98df578e2c39d07ddce6773" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a11b94240946fa8f0549e5cf1c6505b7fa7e0a16#a11b94240946fa8f0549e5cf1c6505b7fa7e0a16" dependencies = [ "anyhow", "bytes", @@ -1159,7 +1159,7 @@ dependencies = [ [[package]] name = "client-api" version = "0.2.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=873415478ed58686c98df578e2c39d07ddce6773#873415478ed58686c98df578e2c39d07ddce6773" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a11b94240946fa8f0549e5cf1c6505b7fa7e0a16#a11b94240946fa8f0549e5cf1c6505b7fa7e0a16" dependencies = [ "again", "anyhow", @@ -1214,7 +1214,7 @@ dependencies = [ [[package]] name = "client-api-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=873415478ed58686c98df578e2c39d07ddce6773#873415478ed58686c98df578e2c39d07ddce6773" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a11b94240946fa8f0549e5cf1c6505b7fa7e0a16#a11b94240946fa8f0549e5cf1c6505b7fa7e0a16" dependencies = [ "collab-entity", "collab-rt-entity", @@ -1227,7 +1227,7 @@ dependencies = [ [[package]] name = "client-websocket" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=873415478ed58686c98df578e2c39d07ddce6773#873415478ed58686c98df578e2c39d07ddce6773" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a11b94240946fa8f0549e5cf1c6505b7fa7e0a16#a11b94240946fa8f0549e5cf1c6505b7fa7e0a16" dependencies = [ "futures-channel", "futures-util", @@ -1499,7 +1499,7 @@ dependencies = [ [[package]] name = "collab-rt-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=873415478ed58686c98df578e2c39d07ddce6773#873415478ed58686c98df578e2c39d07ddce6773" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a11b94240946fa8f0549e5cf1c6505b7fa7e0a16#a11b94240946fa8f0549e5cf1c6505b7fa7e0a16" dependencies = [ "anyhow", "bincode", @@ -1521,7 +1521,7 @@ dependencies = [ [[package]] name = "collab-rt-protocol" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=873415478ed58686c98df578e2c39d07ddce6773#873415478ed58686c98df578e2c39d07ddce6773" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a11b94240946fa8f0549e5cf1c6505b7fa7e0a16#a11b94240946fa8f0549e5cf1c6505b7fa7e0a16" dependencies = [ "anyhow", "async-trait", @@ -1786,7 +1786,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf 0.11.2", + "phf 0.8.0", "smallvec", ] @@ -1969,7 +1969,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "database-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=873415478ed58686c98df578e2c39d07ddce6773#873415478ed58686c98df578e2c39d07ddce6773" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a11b94240946fa8f0549e5cf1c6505b7fa7e0a16#a11b94240946fa8f0549e5cf1c6505b7fa7e0a16" dependencies = [ "bincode", "bytes", @@ -3459,7 +3459,7 @@ dependencies = [ [[package]] name = "gotrue" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=873415478ed58686c98df578e2c39d07ddce6773#873415478ed58686c98df578e2c39d07ddce6773" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a11b94240946fa8f0549e5cf1c6505b7fa7e0a16#a11b94240946fa8f0549e5cf1c6505b7fa7e0a16" dependencies = [ "anyhow", "getrandom 0.2.10", @@ -3474,7 +3474,7 @@ dependencies = [ [[package]] name = "gotrue-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=873415478ed58686c98df578e2c39d07ddce6773#873415478ed58686c98df578e2c39d07ddce6773" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a11b94240946fa8f0549e5cf1c6505b7fa7e0a16#a11b94240946fa8f0549e5cf1c6505b7fa7e0a16" dependencies = [ "app-error", "jsonwebtoken", @@ -4098,7 +4098,7 @@ dependencies = [ [[package]] name = "infra" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=873415478ed58686c98df578e2c39d07ddce6773#873415478ed58686c98df578e2c39d07ddce6773" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a11b94240946fa8f0549e5cf1c6505b7fa7e0a16#a11b94240946fa8f0549e5cf1c6505b7fa7e0a16" dependencies = [ "anyhow", "bytes", @@ -5189,7 +5189,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" dependencies = [ - "phf_macros 0.8.0", + "phf_macros", "phf_shared 0.8.0", "proc-macro-hack", ] @@ -5209,7 +5209,6 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ - "phf_macros 0.11.3", "phf_shared 0.11.2", ] @@ -5277,19 +5276,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "phf_macros" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" -dependencies = [ - "phf_generator 0.11.2", - "phf_shared 0.11.2", - "proc-macro2", - "quote", - "syn 2.0.94", -] - [[package]] name = "phf_shared" version = "0.8.0" @@ -6784,7 +6770,7 @@ dependencies = [ [[package]] name = "shared-entity" version = "0.1.0" -source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=873415478ed58686c98df578e2c39d07ddce6773#873415478ed58686c98df578e2c39d07ddce6773" +source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=a11b94240946fa8f0549e5cf1c6505b7fa7e0a16#a11b94240946fa8f0549e5cf1c6505b7fa7e0a16" dependencies = [ "anyhow", "app-error", diff --git a/frontend/rust-lib/Cargo.toml b/frontend/rust-lib/Cargo.toml index 8d8e11f6a0..4166331fd9 100644 --- a/frontend/rust-lib/Cargo.toml +++ b/frontend/rust-lib/Cargo.toml @@ -105,8 +105,8 @@ tantivy = { version = "0.24.0" } # Run the script.add_workspace_members: # scripts/tool/update_client_api_rev.sh new_rev_id # ⚠️⚠️⚠️️ -client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "873415478ed58686c98df578e2c39d07ddce6773" } -client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "873415478ed58686c98df578e2c39d07ddce6773" } +client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "a11b94240946fa8f0549e5cf1c6505b7fa7e0a16" } +client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "a11b94240946fa8f0549e5cf1c6505b7fa7e0a16" } [profile.dev] opt-level = 0 diff --git a/frontend/rust-lib/flowy-search/src/document/handler.rs b/frontend/rust-lib/flowy-search/src/document/handler.rs index fc68f850e5..d236f4b639 100644 --- a/frontend/rust-lib/flowy-search/src/document/handler.rs +++ b/frontend/rust-lib/flowy-search/src/document/handler.rs @@ -77,6 +77,12 @@ impl SearchHandler for DocumentSearchHandler { }; // Execute document search. + yield Ok( + CreateSearchResultPBArgs::default().searching(true) + .build() + .unwrap(), + ); + let result_items = match cloud_service.document_search(&workspace_id, query.clone()).await { Ok(items) => items, Err(e) => { @@ -114,7 +120,9 @@ impl SearchHandler for DocumentSearchHandler { let search_result = RepeatedSearchResponseItemPB { items }; yield Ok( CreateSearchResultPBArgs::default() + .searching(false) .search_result(Some(search_result)) + .generating_ai_summary(true) .build() .unwrap(), ); @@ -138,7 +146,7 @@ impl SearchHandler for DocumentSearchHandler { }) .collect(); - SearchSummaryPB { content: v.content, sources } + SearchSummaryPB { content: v.content, sources, highlights: v.highlights } }) .collect(); @@ -146,12 +154,19 @@ impl SearchHandler for DocumentSearchHandler { yield Ok( CreateSearchResultPBArgs::default() .search_summary(Some(summary_result)) + .generating_ai_summary(false) .build() .unwrap(), ); } Err(e) => { warn!("Failed to generate search summary: {:?}", e); + yield Ok( + CreateSearchResultPBArgs::default() + .generating_ai_summary(false) + .build() + .unwrap(), + ); } } }) diff --git a/frontend/rust-lib/flowy-search/src/entities/notification.rs b/frontend/rust-lib/flowy-search/src/entities/notification.rs index 3f1cdab67a..4f12305d9a 100644 --- a/frontend/rust-lib/flowy-search/src/entities/notification.rs +++ b/frontend/rust-lib/flowy-search/src/entities/notification.rs @@ -8,9 +8,6 @@ pub struct SearchStatePB { #[pb(index = 2)] pub search_id: String, - - #[pb(index = 3)] - pub is_loading: bool, } #[derive(ProtoBuf_Enum, Debug, Default)] diff --git a/frontend/rust-lib/flowy-search/src/entities/result.rs b/frontend/rust-lib/flowy-search/src/entities/result.rs index 8f5ba11ded..a01f01b074 100644 --- a/frontend/rust-lib/flowy-search/src/entities/result.rs +++ b/frontend/rust-lib/flowy-search/src/entities/result.rs @@ -18,6 +18,14 @@ pub struct SearchResponsePB { #[pb(index = 3, one_of)] #[builder(default)] pub local_search_result: Option, + + #[pb(index = 4)] + #[builder(default)] + pub searching: bool, + + #[pb(index = 5)] + #[builder(default)] + pub generating_ai_summary: bool, } #[derive(ProtoBuf, Default, Debug, Clone)] @@ -33,6 +41,9 @@ pub struct SearchSummaryPB { #[pb(index = 2)] pub sources: Vec, + + #[pb(index = 3)] + pub highlights: String, } #[derive(ProtoBuf, Default, Debug, Clone)] diff --git a/frontend/rust-lib/flowy-search/src/services/manager.rs b/frontend/rust-lib/flowy-search/src/services/manager.rs index 72a3c12793..a71449d5d2 100644 --- a/frontend/rust-lib/flowy-search/src/services/manager.rs +++ b/frontend/rust-lib/flowy-search/src/services/manager.rs @@ -93,7 +93,6 @@ impl SearchManager { let resp = SearchStatePB { response: Some(search_result), search_id: search_id.clone(), - is_loading: true, }; if let Ok::, _>(data) = resp.try_into() { if let Err(err) = clone_sink.send(data).await { @@ -111,7 +110,6 @@ impl SearchManager { let resp = SearchStatePB { response: None, search_id: search_id.clone(), - is_loading: true, }; if let Ok::, _>(data) = resp.try_into() { let _ = clone_sink.send(data).await;