feat(core): quick search support search locally (#12987)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Introduced a "search locally" option in the docs quick search,
allowing users to perform searches on their local device when supported.
* Added new quick search group labels and options for local search, with
dynamic UI updates based on search mode.
* **Improvements**
  * Enhanced search responsiveness by reducing input throttling delay.
  * Added a pre-submission check to improve search item handling.
* Improved stability by handling cases where document IDs may be missing
during search result processing.
* **Localization**
* Added English language support for new local search options and
labels.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
EYHN
2025-07-03 11:11:55 +08:00
committed by GitHub
parent aa7edb7255
commit 4641b080f2
11 changed files with 101 additions and 16 deletions
@@ -10,6 +10,7 @@ import {
QuickSearchTagIcon,
} from '@affine/core/modules/quicksearch';
import { TagService } from '@affine/core/modules/tag';
import { UserFriendlyError } from '@affine/error';
import { useI18n } from '@affine/i18n';
import { sleep } from '@blocksuite/affine/global/utils';
import { ViewLayersIcon } from '@blocksuite/icons/rc';
@@ -105,13 +106,17 @@ const WithQueryList = () => {
const docList = useLiveData(searchService.docs.items$);
const tagList = useLiveData(searchService.tags.items$);
const error = useLiveData(searchService.docs.error$);
const docs = useMemo(
() =>
docList.map(item => ({
id: item.payload.docId,
icon: item.icon,
title: <SearchResLabel item={item} />,
})),
docList
.filter(item => item.id !== 'search-locally')
.map(item => ({
id: item.payload.docId,
icon: item.icon,
title: <SearchResLabel item={item} />,
})),
[docList]
);
@@ -121,6 +126,7 @@ const WithQueryList = () => {
docs={docs}
collections={collectionList}
tags={tagList}
error={error ? UserFriendlyError.fromAny(error).message : null}
/>
);
};
@@ -12,6 +12,7 @@ export interface SearchResultsProps {
docs?: DocCardProps['meta'][];
collections?: UniversalSearchResultItemProps['item'][];
tags?: UniversalSearchResultItemProps['item'][];
error?: any;
}
const Empty = () => {
@@ -26,11 +27,14 @@ export const SearchResults = ({
docs,
collections,
tags,
error,
}: SearchResultsProps) => {
return (
<>
<div className={styles.resTitle}>{title}</div>
{error && <p className={styles.errorMessage}>{error}</p>}
{!docs?.length && !collections?.length && !tags?.length ? (
<Empty />
) : null}
@@ -1,3 +1,4 @@
import { cssVar } from '@toeverything/theme';
import {
bodyEmphasized,
footnoteRegular,
@@ -84,3 +85,9 @@ export const empty = style([
color: cssVarV2('text/primary'),
},
]);
export const errorMessage = style({
padding: '0px 16px 16px',
fontSize: cssVar('fontSm'),
color: cssVarV2('status/error'),
});