chore: loading for search summary

This commit is contained in:
Nathan
2025-04-15 15:59:15 +08:00
parent d01909830d
commit 3b3ae7fde9
13 changed files with 250 additions and 93 deletions

View File

@ -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<SearchResponseItemPB>? serverItems,
List<LocalSearchResponseItemPB>? localItems,
List<SearchSummaryPB>? 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<SearchResponseItemPB>? serverItems,
List<LocalSearchResponseItemPB>? localItems,
List<SearchSummaryPB>? summaries,
@ -324,12 +335,14 @@ class CommandPaletteState with _$CommandPaletteState {
@Default({}) Map<String, SearchResultItem> combinedResponseItems,
@Default([]) List<SearchSummaryPB> resultSummaries,
@Default(null) SearchResponseStream? searchResponseStream,
required bool isLoading,
required bool searching,
required bool generatingAIOverview,
@Default([]) List<TrashPB> trash,
@Default(null) String? searchId,
}) = _CommandPaletteState;
factory CommandPaletteState.initial() => const CommandPaletteState(
isLoading: false,
searching: false,
generatingAIOverview: false,
);
}

View File

@ -49,12 +49,14 @@ class SearchResponseStream {
void Function(
List<SearchResponseItemPB> items,
String searchId,
bool isLoading,
bool searching,
bool generatingAIOverview,
)? _onServerItems;
void Function(
List<SearchSummaryPB> 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<SearchResponseItemPB> items,
String searchId,
bool isLoading,
bool generatingAIOverview,
)? onServerItems,
required void Function(
List<SearchSummaryPB> summaries,
String searchId,
bool isLoading,
bool generatingAIOverview,
)? onSummaries,
required void Function(
List<LocalSearchResponseItemPB> items,

View File

@ -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(),

View File

@ -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(

View File

@ -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<CommandPaletteBloc>().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<SearchResultListBloc, SearchResultListState>(
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),
),
),
);
}
}

View File

@ -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<SearchResultListBloc>().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<SearchResultListBloc>().add(
SearchResultListEvent.openPage(pageId: source.id),
);
},
),
),
);
}

View File

@ -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...",

View File

@ -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",

View File

@ -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

View File

@ -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(),
);
}
}
})

View File

@ -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)]

View File

@ -18,6 +18,14 @@ pub struct SearchResponsePB {
#[pb(index = 3, one_of)]
#[builder(default)]
pub local_search_result: Option<RepeatedLocalSearchResponseItemPB>,
#[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<SearchSourcePB>,
#[pb(index = 3)]
pub highlights: String,
}
#[derive(ProtoBuf, Default, Debug, Clone)]

View File

@ -93,7 +93,6 @@ impl SearchManager {
let resp = SearchStatePB {
response: Some(search_result),
search_id: search_id.clone(),
is_loading: true,
};
if let Ok::<Vec<u8>, _>(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::<Vec<u8>, _>(data) = resp.try_into() {
let _ = clone_sink.send(data).await;