mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-09-04 14:25:32 +00:00
chore: loading for search summary
This commit is contained in:
parent
d01909830d
commit
3b3ae7fde9
@ -94,15 +94,16 @@ class CommandPaletteBloc
|
|||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
query: null,
|
query: null,
|
||||||
isLoading: false,
|
searching: false,
|
||||||
serverResponseItems: [],
|
serverResponseItems: [],
|
||||||
localResponseItems: [],
|
localResponseItems: [],
|
||||||
combinedResponseItems: {},
|
combinedResponseItems: {},
|
||||||
resultSummaries: [],
|
resultSummaries: [],
|
||||||
|
generatingAIOverview: false,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
emit(state.copyWith(query: event.search, isLoading: true));
|
emit(state.copyWith(query: event.search, searching: true));
|
||||||
_activeQuery = event.search;
|
_activeQuery = event.search;
|
||||||
|
|
||||||
unawaited(
|
unawaited(
|
||||||
@ -122,7 +123,8 @@ class CommandPaletteBloc
|
|||||||
add(
|
add(
|
||||||
CommandPaletteEvent.resultsChanged(
|
CommandPaletteEvent.resultsChanged(
|
||||||
searchId: '',
|
searchId: '',
|
||||||
isLoading: false,
|
searching: false,
|
||||||
|
generatingAIOverview: false,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -150,19 +152,23 @@ class CommandPaletteBloc
|
|||||||
searchId: searchId,
|
searchId: searchId,
|
||||||
localItems: items,
|
localItems: items,
|
||||||
),
|
),
|
||||||
onServerItems: (items, searchId, isLoading) => _handleResultsUpdate(
|
onServerItems: (items, searchId, searching, generatingAIOverview) =>
|
||||||
|
_handleResultsUpdate(
|
||||||
searchId: searchId,
|
searchId: searchId,
|
||||||
serverItems: items,
|
serverItems: items,
|
||||||
isLoading: isLoading,
|
searching: searching,
|
||||||
|
generatingAIOverview: generatingAIOverview,
|
||||||
),
|
),
|
||||||
onSummaries: (summaries, searchId, isLoading) => _handleResultsUpdate(
|
onSummaries: (summaries, searchId, searching, generatingAIOverview) =>
|
||||||
|
_handleResultsUpdate(
|
||||||
searchId: searchId,
|
searchId: searchId,
|
||||||
summaries: summaries,
|
summaries: summaries,
|
||||||
isLoading: isLoading,
|
searching: searching,
|
||||||
|
generatingAIOverview: generatingAIOverview,
|
||||||
),
|
),
|
||||||
onFinished: (searchId) => _handleResultsUpdate(
|
onFinished: (searchId) => _handleResultsUpdate(
|
||||||
searchId: searchId,
|
searchId: searchId,
|
||||||
isLoading: false,
|
searching: false,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -172,7 +178,8 @@ class CommandPaletteBloc
|
|||||||
List<SearchResponseItemPB>? serverItems,
|
List<SearchResponseItemPB>? serverItems,
|
||||||
List<LocalSearchResponseItemPB>? localItems,
|
List<LocalSearchResponseItemPB>? localItems,
|
||||||
List<SearchSummaryPB>? summaries,
|
List<SearchSummaryPB>? summaries,
|
||||||
bool isLoading = true,
|
bool searching = true,
|
||||||
|
bool generatingAIOverview = false,
|
||||||
}) {
|
}) {
|
||||||
if (_isActiveSearch(searchId)) {
|
if (_isActiveSearch(searchId)) {
|
||||||
add(
|
add(
|
||||||
@ -181,7 +188,8 @@ class CommandPaletteBloc
|
|||||||
serverItems: serverItems,
|
serverItems: serverItems,
|
||||||
localItems: localItems,
|
localItems: localItems,
|
||||||
summaries: summaries,
|
summaries: summaries,
|
||||||
isLoading: isLoading,
|
searching: searching,
|
||||||
|
generatingAIOverview: generatingAIOverview,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -223,7 +231,8 @@ class CommandPaletteBloc
|
|||||||
localResponseItems: event.localItems ?? state.localResponseItems,
|
localResponseItems: event.localItems ?? state.localResponseItems,
|
||||||
resultSummaries: event.summaries ?? state.resultSummaries,
|
resultSummaries: event.summaries ?? state.resultSummaries,
|
||||||
combinedResponseItems: combinedItems,
|
combinedResponseItems: combinedItems,
|
||||||
isLoading: event.isLoading,
|
searching: event.searching,
|
||||||
|
generatingAIOverview: event.generatingAIOverview,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -256,7 +265,8 @@ class CommandPaletteBloc
|
|||||||
localResponseItems: [],
|
localResponseItems: [],
|
||||||
combinedResponseItems: {},
|
combinedResponseItems: {},
|
||||||
resultSummaries: [],
|
resultSummaries: [],
|
||||||
isLoading: false,
|
searching: false,
|
||||||
|
generatingAIOverview: false,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -283,7 +293,8 @@ class CommandPaletteEvent with _$CommandPaletteEvent {
|
|||||||
}) = _NewSearchStream;
|
}) = _NewSearchStream;
|
||||||
const factory CommandPaletteEvent.resultsChanged({
|
const factory CommandPaletteEvent.resultsChanged({
|
||||||
required String searchId,
|
required String searchId,
|
||||||
required bool isLoading,
|
required bool searching,
|
||||||
|
required bool generatingAIOverview,
|
||||||
List<SearchResponseItemPB>? serverItems,
|
List<SearchResponseItemPB>? serverItems,
|
||||||
List<LocalSearchResponseItemPB>? localItems,
|
List<LocalSearchResponseItemPB>? localItems,
|
||||||
List<SearchSummaryPB>? summaries,
|
List<SearchSummaryPB>? summaries,
|
||||||
@ -324,12 +335,14 @@ class CommandPaletteState with _$CommandPaletteState {
|
|||||||
@Default({}) Map<String, SearchResultItem> combinedResponseItems,
|
@Default({}) Map<String, SearchResultItem> combinedResponseItems,
|
||||||
@Default([]) List<SearchSummaryPB> resultSummaries,
|
@Default([]) List<SearchSummaryPB> resultSummaries,
|
||||||
@Default(null) SearchResponseStream? searchResponseStream,
|
@Default(null) SearchResponseStream? searchResponseStream,
|
||||||
required bool isLoading,
|
required bool searching,
|
||||||
|
required bool generatingAIOverview,
|
||||||
@Default([]) List<TrashPB> trash,
|
@Default([]) List<TrashPB> trash,
|
||||||
@Default(null) String? searchId,
|
@Default(null) String? searchId,
|
||||||
}) = _CommandPaletteState;
|
}) = _CommandPaletteState;
|
||||||
|
|
||||||
factory CommandPaletteState.initial() => const CommandPaletteState(
|
factory CommandPaletteState.initial() => const CommandPaletteState(
|
||||||
isLoading: false,
|
searching: false,
|
||||||
|
generatingAIOverview: false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -49,12 +49,14 @@ class SearchResponseStream {
|
|||||||
void Function(
|
void Function(
|
||||||
List<SearchResponseItemPB> items,
|
List<SearchResponseItemPB> items,
|
||||||
String searchId,
|
String searchId,
|
||||||
bool isLoading,
|
bool searching,
|
||||||
|
bool generatingAIOverview,
|
||||||
)? _onServerItems;
|
)? _onServerItems;
|
||||||
void Function(
|
void Function(
|
||||||
List<SearchSummaryPB> summaries,
|
List<SearchSummaryPB> summaries,
|
||||||
String searchId,
|
String searchId,
|
||||||
bool isLoading,
|
bool searching,
|
||||||
|
bool generatingAIOverview,
|
||||||
)? _onSummaries;
|
)? _onSummaries;
|
||||||
|
|
||||||
void Function(
|
void Function(
|
||||||
@ -78,14 +80,16 @@ class SearchResponseStream {
|
|||||||
_onServerItems?.call(
|
_onServerItems?.call(
|
||||||
searchState.response.searchResult.items,
|
searchState.response.searchResult.items,
|
||||||
searchId,
|
searchId,
|
||||||
searchState.isLoading,
|
searchState.response.searching,
|
||||||
|
searchState.response.generatingAiSummary,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (searchState.response.hasSearchSummary()) {
|
if (searchState.response.hasSearchSummary()) {
|
||||||
_onSummaries?.call(
|
_onSummaries?.call(
|
||||||
searchState.response.searchSummary.items,
|
searchState.response.searchSummary.items,
|
||||||
searchId,
|
searchId,
|
||||||
searchState.isLoading,
|
searchState.response.searching,
|
||||||
|
searchState.response.generatingAiSummary,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,11 +109,13 @@ class SearchResponseStream {
|
|||||||
List<SearchResponseItemPB> items,
|
List<SearchResponseItemPB> items,
|
||||||
String searchId,
|
String searchId,
|
||||||
bool isLoading,
|
bool isLoading,
|
||||||
|
bool generatingAIOverview,
|
||||||
)? onServerItems,
|
)? onServerItems,
|
||||||
required void Function(
|
required void Function(
|
||||||
List<SearchSummaryPB> summaries,
|
List<SearchSummaryPB> summaries,
|
||||||
String searchId,
|
String searchId,
|
||||||
bool isLoading,
|
bool isLoading,
|
||||||
|
bool generatingAIOverview,
|
||||||
)? onSummaries,
|
)? onSummaries,
|
||||||
required void Function(
|
required void Function(
|
||||||
List<LocalSearchResponseItemPB> items,
|
List<LocalSearchResponseItemPB> items,
|
||||||
|
@ -144,7 +144,7 @@ class CommandPaletteModal extends StatelessWidget {
|
|||||||
// Change mainAxisSize to max so Expanded works correctly.
|
// Change mainAxisSize to max so Expanded works correctly.
|
||||||
Column(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
SearchField(query: state.query, isLoading: state.isLoading),
|
SearchField(query: state.query, isLoading: state.searching),
|
||||||
if (state.query?.isEmpty ?? true) ...[
|
if (state.query?.isEmpty ?? true) ...[
|
||||||
const Divider(height: 0),
|
const Divider(height: 0),
|
||||||
Flexible(
|
Flexible(
|
||||||
@ -167,7 +167,7 @@ class CommandPaletteModal extends StatelessWidget {
|
|||||||
// When there are no results and the query is not empty and not loading,
|
// When there are no results and the query is not empty and not loading,
|
||||||
// show the no results message, centered in the available space.
|
// show the no results message, centered in the available space.
|
||||||
else if ((state.query?.isNotEmpty ?? false) &&
|
else if ((state.query?.isNotEmpty ?? false) &&
|
||||||
!state.isLoading) ...[
|
!state.searching) ...[
|
||||||
const Divider(height: 0),
|
const Divider(height: 0),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: const _NoResultsHint(),
|
child: const _NoResultsHint(),
|
||||||
|
@ -207,9 +207,12 @@ class SearchResultPreview extends StatelessWidget {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
FlowyText(LocaleKeys.commandPalette_pagePreview.tr()),
|
Opacity(
|
||||||
const Divider(
|
opacity: 0.5,
|
||||||
thickness: 1,
|
child: FlowyText(
|
||||||
|
LocaleKeys.commandPalette_pagePreview.tr(),
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const VSpace(6),
|
const VSpace(6),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -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/action_navigation/navigation_action.dart';
|
||||||
import 'package:appflowy/workspace/application/command_palette/command_palette_bloc.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/workspace/application/command_palette/search_result_list_bloc.dart';
|
||||||
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:appflowy/generated/locale_keys.g.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:easy_localization/easy_localization.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
|
|
||||||
import 'search_result_cell.dart';
|
import 'search_result_cell.dart';
|
||||||
import 'search_summary_cell.dart';
|
import 'search_summary_cell.dart';
|
||||||
@ -35,7 +37,19 @@ class SearchResultList extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSummariesSection() {
|
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(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -53,6 +67,9 @@ class SearchResultList extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildResultsSection(BuildContext context) {
|
Widget _buildResultsSection(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -83,6 +100,8 @@ class SearchResultList extends StatelessWidget {
|
|||||||
child: BlocProvider(
|
child: BlocProvider(
|
||||||
create: (context) => SearchResultListBloc(),
|
create: (context) => SearchResultListBloc(),
|
||||||
child: BlocListener<SearchResultListBloc, SearchResultListState>(
|
child: BlocListener<SearchResultListBloc, SearchResultListState>(
|
||||||
|
listenWhen: (previous, current) =>
|
||||||
|
previous.openPageId != current.openPageId,
|
||||||
listener: (context, state) {
|
listener: (context, state) {
|
||||||
if (state.openPageId != null) {
|
if (state.openPageId != null) {
|
||||||
FlowyOverlay.pop(context);
|
FlowyOverlay.pop(context);
|
||||||
@ -102,25 +121,29 @@ class SearchResultList extends StatelessWidget {
|
|||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: const ClampingScrollPhysics(),
|
physics: const ClampingScrollPhysics(),
|
||||||
children: [
|
children: [
|
||||||
if (resultSummaries.isNotEmpty) _buildSummariesSection(),
|
_buildAIOverviewSection(context),
|
||||||
const VSpace(10),
|
const VSpace(10),
|
||||||
if (resultItems.isNotEmpty) _buildResultsSection(context),
|
if (resultItems.isNotEmpty) _buildResultsSection(context),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const HSpace(10),
|
const HSpace(10),
|
||||||
if (resultItems.any((item) => item.content.isNotEmpty))
|
if (resultItems.any((item) => item.content.isNotEmpty)) ...[
|
||||||
|
const VerticalDivider(
|
||||||
|
thickness: 1.0,
|
||||||
|
),
|
||||||
Flexible(
|
Flexible(
|
||||||
flex: 3,
|
flex: 3,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 12,
|
horizontal: 8,
|
||||||
vertical: 16,
|
vertical: 16,
|
||||||
),
|
),
|
||||||
child: const SearchCellPreview(),
|
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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
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_ext.dart';
|
||||||
import 'package:appflowy/workspace/application/command_palette/search_result_list_bloc.dart';
|
import 'package:appflowy/workspace/application/command_palette/search_result_list_bloc.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
@ -56,17 +57,50 @@ class SearchSummaryPreview extends StatelessWidget {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
FlowyText(LocaleKeys.commandPalette_aiOverviewSource.tr()),
|
if (summary.highlights.isNotEmpty) ...[
|
||||||
const Divider(
|
Opacity(
|
||||||
thickness: 1,
|
opacity: 0.5,
|
||||||
|
child: FlowyText(
|
||||||
|
LocaleKeys.commandPalette_aiOverviewHighlights.tr(),
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const VSpace(6),
|
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)),
|
...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 {
|
class SearchSummarySource extends StatelessWidget {
|
||||||
const SearchSummarySource({
|
const SearchSummarySource({
|
||||||
required this.source,
|
required this.source,
|
||||||
@ -78,7 +112,9 @@ class SearchSummarySource extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final icon = source.icon.getIcon();
|
final icon = source.icon.getIcon();
|
||||||
return SizedBox(
|
return FlowyTooltip(
|
||||||
|
message: LocaleKeys.commandPalette_clickToOpenPage.tr(),
|
||||||
|
child: SizedBox(
|
||||||
height: 30,
|
height: 30,
|
||||||
child: FlowyButton(
|
child: FlowyButton(
|
||||||
leftIcon: icon,
|
leftIcon: icon,
|
||||||
@ -91,6 +127,7 @@ class SearchSummarySource extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2699,7 +2699,9 @@
|
|||||||
"bestMatches": "Best matches",
|
"bestMatches": "Best matches",
|
||||||
"aiOverview": "AI overview",
|
"aiOverview": "AI overview",
|
||||||
"aiOverviewSource": "Reference sources",
|
"aiOverviewSource": "Reference sources",
|
||||||
|
"aiOverviewHighlights": "Highlights",
|
||||||
"pagePreview": "Content preview",
|
"pagePreview": "Content preview",
|
||||||
|
"clickToOpenPage": "Click to open page",
|
||||||
"recentHistory": "Recent history",
|
"recentHistory": "Recent history",
|
||||||
"navigateHint": "to navigate",
|
"navigateHint": "to navigate",
|
||||||
"loadingTooltip": "We are looking for results...",
|
"loadingTooltip": "We are looking for results...",
|
||||||
|
42
frontend/rust-lib/Cargo.lock
generated
42
frontend/rust-lib/Cargo.lock
generated
@ -493,7 +493,7 @@ checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "app-error"
|
name = "app-error"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -513,7 +513,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy-ai-client"
|
name = "appflowy-ai-client"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -1159,7 +1159,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api"
|
name = "client-api"
|
||||||
version = "0.2.0"
|
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 = [
|
dependencies = [
|
||||||
"again",
|
"again",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -1214,7 +1214,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api-entity"
|
name = "client-api-entity"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"collab-entity",
|
"collab-entity",
|
||||||
"collab-rt-entity",
|
"collab-rt-entity",
|
||||||
@ -1227,7 +1227,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-websocket"
|
name = "client-websocket"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -1499,7 +1499,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-rt-entity"
|
name = "collab-rt-entity"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -1521,7 +1521,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-rt-protocol"
|
name = "collab-rt-protocol"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1786,7 +1786,7 @@ dependencies = [
|
|||||||
"cssparser-macros",
|
"cssparser-macros",
|
||||||
"dtoa-short",
|
"dtoa-short",
|
||||||
"itoa",
|
"itoa",
|
||||||
"phf 0.11.2",
|
"phf 0.8.0",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1969,7 +1969,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "database-entity"
|
name = "database-entity"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -3459,7 +3459,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue"
|
name = "gotrue"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"getrandom 0.2.10",
|
"getrandom 0.2.10",
|
||||||
@ -3474,7 +3474,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue-entity"
|
name = "gotrue-entity"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"app-error",
|
"app-error",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
@ -4098,7 +4098,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "infra"
|
name = "infra"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -5189,7 +5189,7 @@ version = "0.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_macros 0.8.0",
|
"phf_macros",
|
||||||
"phf_shared 0.8.0",
|
"phf_shared 0.8.0",
|
||||||
"proc-macro-hack",
|
"proc-macro-hack",
|
||||||
]
|
]
|
||||||
@ -5209,7 +5209,6 @@ version = "0.11.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_macros 0.11.3",
|
|
||||||
"phf_shared 0.11.2",
|
"phf_shared 0.11.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -5277,19 +5276,6 @@ dependencies = [
|
|||||||
"syn 1.0.109",
|
"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]]
|
[[package]]
|
||||||
name = "phf_shared"
|
name = "phf_shared"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@ -6784,7 +6770,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "shared-entity"
|
name = "shared-entity"
|
||||||
version = "0.1.0"
|
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 = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"app-error",
|
"app-error",
|
||||||
|
@ -105,8 +105,8 @@ tantivy = { version = "0.24.0" }
|
|||||||
# Run the script.add_workspace_members:
|
# Run the script.add_workspace_members:
|
||||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
client-api = { 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 = "873415478ed58686c98df578e2c39d07ddce6773" }
|
client-api-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "a11b94240946fa8f0549e5cf1c6505b7fa7e0a16" }
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 0
|
opt-level = 0
|
||||||
|
@ -77,6 +77,12 @@ impl SearchHandler for DocumentSearchHandler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Execute document search.
|
// Execute document search.
|
||||||
|
yield Ok(
|
||||||
|
CreateSearchResultPBArgs::default().searching(true)
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
let result_items = match cloud_service.document_search(&workspace_id, query.clone()).await {
|
let result_items = match cloud_service.document_search(&workspace_id, query.clone()).await {
|
||||||
Ok(items) => items,
|
Ok(items) => items,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -114,7 +120,9 @@ impl SearchHandler for DocumentSearchHandler {
|
|||||||
let search_result = RepeatedSearchResponseItemPB { items };
|
let search_result = RepeatedSearchResponseItemPB { items };
|
||||||
yield Ok(
|
yield Ok(
|
||||||
CreateSearchResultPBArgs::default()
|
CreateSearchResultPBArgs::default()
|
||||||
|
.searching(false)
|
||||||
.search_result(Some(search_result))
|
.search_result(Some(search_result))
|
||||||
|
.generating_ai_summary(true)
|
||||||
.build()
|
.build()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
@ -138,7 +146,7 @@ impl SearchHandler for DocumentSearchHandler {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
SearchSummaryPB { content: v.content, sources }
|
SearchSummaryPB { content: v.content, sources, highlights: v.highlights }
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@ -146,12 +154,19 @@ impl SearchHandler for DocumentSearchHandler {
|
|||||||
yield Ok(
|
yield Ok(
|
||||||
CreateSearchResultPBArgs::default()
|
CreateSearchResultPBArgs::default()
|
||||||
.search_summary(Some(summary_result))
|
.search_summary(Some(summary_result))
|
||||||
|
.generating_ai_summary(false)
|
||||||
.build()
|
.build()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Failed to generate search summary: {:?}", e);
|
warn!("Failed to generate search summary: {:?}", e);
|
||||||
|
yield Ok(
|
||||||
|
CreateSearchResultPBArgs::default()
|
||||||
|
.generating_ai_summary(false)
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -8,9 +8,6 @@ pub struct SearchStatePB {
|
|||||||
|
|
||||||
#[pb(index = 2)]
|
#[pb(index = 2)]
|
||||||
pub search_id: String,
|
pub search_id: String,
|
||||||
|
|
||||||
#[pb(index = 3)]
|
|
||||||
pub is_loading: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(ProtoBuf_Enum, Debug, Default)]
|
#[derive(ProtoBuf_Enum, Debug, Default)]
|
||||||
|
@ -18,6 +18,14 @@ pub struct SearchResponsePB {
|
|||||||
#[pb(index = 3, one_of)]
|
#[pb(index = 3, one_of)]
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
pub local_search_result: Option<RepeatedLocalSearchResponseItemPB>,
|
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)]
|
#[derive(ProtoBuf, Default, Debug, Clone)]
|
||||||
@ -33,6 +41,9 @@ pub struct SearchSummaryPB {
|
|||||||
|
|
||||||
#[pb(index = 2)]
|
#[pb(index = 2)]
|
||||||
pub sources: Vec<SearchSourcePB>,
|
pub sources: Vec<SearchSourcePB>,
|
||||||
|
|
||||||
|
#[pb(index = 3)]
|
||||||
|
pub highlights: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(ProtoBuf, Default, Debug, Clone)]
|
#[derive(ProtoBuf, Default, Debug, Clone)]
|
||||||
|
@ -93,7 +93,6 @@ impl SearchManager {
|
|||||||
let resp = SearchStatePB {
|
let resp = SearchStatePB {
|
||||||
response: Some(search_result),
|
response: Some(search_result),
|
||||||
search_id: search_id.clone(),
|
search_id: search_id.clone(),
|
||||||
is_loading: true,
|
|
||||||
};
|
};
|
||||||
if let Ok::<Vec<u8>, _>(data) = resp.try_into() {
|
if let Ok::<Vec<u8>, _>(data) = resp.try_into() {
|
||||||
if let Err(err) = clone_sink.send(data).await {
|
if let Err(err) = clone_sink.send(data).await {
|
||||||
@ -111,7 +110,6 @@ impl SearchManager {
|
|||||||
let resp = SearchStatePB {
|
let resp = SearchStatePB {
|
||||||
response: None,
|
response: None,
|
||||||
search_id: search_id.clone(),
|
search_id: search_id.clone(),
|
||||||
is_loading: true,
|
|
||||||
};
|
};
|
||||||
if let Ok::<Vec<u8>, _>(data) = resp.try_into() {
|
if let Ok::<Vec<u8>, _>(data) = resp.try_into() {
|
||||||
let _ = clone_sink.send(data).await;
|
let _ = clone_sink.send(data).await;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user