akumatus
2025-03-19 04:22:10 +00:00
parent b470eafd55
commit 47206b8d47
12 changed files with 169 additions and 46 deletions

View File

@@ -3,9 +3,9 @@ import { type Framework } from '@toeverything/infra';
import { WorkspaceDialogService } from '../dialogs';
import { DocsService } from '../doc';
import { DocDisplayMetaService } from '../doc-display-meta';
import { DocSearchMenuService } from '../doc-search-menu/services';
import { EditorSettingService } from '../editor-setting';
import { JournalService } from '../journal';
import { SearchMenuService } from '../search-menu/services';
import { WorkspaceScope } from '../workspace';
import { AtMenuConfigService } from './services';
@@ -18,6 +18,6 @@ export function configAtMenuConfigModule(framework: Framework) {
WorkspaceDialogService,
EditorSettingService,
DocsService,
DocSearchMenuService,
SearchMenuService,
]);
}

View File

@@ -24,9 +24,9 @@ import { html } from 'lit';
import type { WorkspaceDialogService } from '../../dialogs';
import type { DocsService } from '../../doc';
import type { DocDisplayMetaService } from '../../doc-display-meta';
import type { DocSearchMenuService } from '../../doc-search-menu/services';
import type { EditorSettingService } from '../../editor-setting';
import { type JournalService, suggestJournalDate } from '../../journal';
import type { SearchMenuService } from '../../search-menu/services';
function resolveSignal<T>(data: T | Signal<T>): T {
return data instanceof Signal ? data.value : data;
@@ -45,7 +45,7 @@ export class AtMenuConfigService extends Service {
private readonly dialogService: WorkspaceDialogService,
private readonly editorSettingService: EditorSettingService,
private readonly docsService: DocsService,
private readonly docsSearchMenuService: DocSearchMenuService
private readonly searchMenuService: SearchMenuService
) {
super();
}
@@ -292,7 +292,7 @@ export class AtMenuConfigService extends Service {
track.doc.editor.atMenu.linkDoc();
this.insertDoc(inlineEditor, meta.id);
};
const result = this.docsSearchMenuService.getDocMenuGroup(
const result = this.searchMenuService.getDocMenuGroup(
query,
action,
abortSignal

View File

@@ -17,7 +17,6 @@ import { configureDocModule } from './doc';
import { configureDocDisplayMetaModule } from './doc-display-meta';
import { configureDocInfoModule } from './doc-info';
import { configureDocLinksModule } from './doc-link';
import { configDocSearchMenuModule } from './doc-search-menu';
import { configureDocsSearchModule } from './docs-search';
import { configureEditorModule } from './editor';
import { configureEditorSettingModule } from './editor-setting';
@@ -39,6 +38,7 @@ import { configurePDFModule } from './pdf';
import { configurePeekViewModule } from './peek-view';
import { configurePermissionsModule } from './permissions';
import { configureQuickSearchModule } from './quicksearch';
import { configSearchMenuModule } from './search-menu';
import { configureShareDocsModule } from './share-doc';
import { configureShareSettingModule } from './share-setting';
import {
@@ -96,7 +96,7 @@ export function configureCommonModules(framework: Framework) {
configureDocInfoModule(framework);
configureOpenInApp(framework);
configAtMenuConfigModule(framework);
configDocSearchMenuModule(framework);
configSearchMenuModule(framework);
configureDndModule(framework);
configureCommonGlobalStorageImpls(framework);
configureAINetworkSearchModule(framework);

View File

@@ -3,16 +3,18 @@ import { type Framework } from '@toeverything/infra';
import { DocDisplayMetaService } from '../doc-display-meta';
import { DocsSearchService } from '../docs-search';
import { RecentDocsService } from '../quicksearch';
import { TagService } from '../tag';
import { WorkspaceScope, WorkspaceService } from '../workspace';
import { DocSearchMenuService } from './services';
import { SearchMenuService } from './services';
export function configDocSearchMenuModule(framework: Framework) {
export function configSearchMenuModule(framework: Framework) {
framework
.scope(WorkspaceScope)
.service(DocSearchMenuService, [
.service(SearchMenuService, [
WorkspaceService,
DocDisplayMetaService,
RecentDocsService,
DocsSearchService,
TagService,
]);
}

View File

@@ -1,3 +1,4 @@
import type { TagMeta } from '@affine/core/components/page-list';
import { fuzzyMatch } from '@affine/core/utils/fuzzy-match';
import { I18n } from '@affine/i18n';
import type {
@@ -9,13 +10,16 @@ import type { DocMeta } from '@blocksuite/affine/store';
import { computed } from '@preact/signals-core';
import { Service } from '@toeverything/infra';
import { cssVarV2 } from '@toeverything/theme/v2';
import Fuse from 'fuse.js';
import { html } from 'lit';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { map, takeWhile } from 'rxjs';
import type { DocDisplayMetaService } from '../../doc-display-meta';
import type { DocsSearchService } from '../../docs-search';
import type { RecentDocsService } from '../../quicksearch';
import { type RecentDocsService } from '../../quicksearch';
import { highlighter } from '../../quicksearch/utils/highlighter';
import type { TagService } from '../../tag';
import type { WorkspaceService } from '../../workspace';
const MAX_DOCS = 3;
@@ -26,12 +30,15 @@ type DocMetaWithHighlights = DocMeta & {
export type SearchDocMenuAction = (meta: DocMeta) => Promise<void> | void;
export class DocSearchMenuService extends Service {
export type SearchTagMenuAction = (tagId: TagMeta) => Promise<void> | void;
export class SearchMenuService extends Service {
constructor(
private readonly workspaceService: WorkspaceService,
private readonly docDisplayMetaService: DocDisplayMetaService,
private readonly recentDocsService: RecentDocsService,
private readonly docsSearch: DocsSearchService
private readonly docsSearch: DocsSearchService,
private readonly tagService: TagService
) {
super();
}
@@ -210,4 +217,81 @@ export class DocSearchMenuService extends Service {
},
};
}
getTagMenuGroup(
query: string,
action: SearchTagMenuAction,
_abortSignal: AbortSignal
): LinkedMenuGroup {
const tags: TagMeta[] = this.tagService.tagList.tagMetas$.value;
if (query.trim().length === 0) {
return {
name: I18n.t('com.affine.editor.at-menu.tags', {
query,
}),
items: tags.map(tag => this.toTagMenuItem(tag, action)),
};
}
const fuse = new Fuse(tags, {
keys: ['title'],
includeMatches: true,
includeScore: true,
ignoreLocation: true,
threshold: 0.0,
});
const result = fuse.search(query);
return {
name: I18n.t('com.affine.editor.at-menu.link-to-doc', {
query,
}),
items: result.map(item => {
const normalizedRange = ([start, end]: [number, number]) =>
[
start,
end + 1 /* in fuse, the `end` is different from the `substring` */,
] as [number, number];
const titleMatches = item.matches
?.filter(match => match.key === 'title')
.flatMap(match => match.indices.map(normalizedRange));
const hTitle = highlighter(
item.item.title,
`<span style="color: ${cssVarV2('text/emphasis')}">`,
'</span>',
titleMatches ?? []
);
return this.toTagMenuItem(
{
...item.item,
title: hTitle ?? item.item.title,
},
action
);
}),
};
}
private toTagMenuItem(
tag: TagMeta,
action: SearchTagMenuAction
): LinkedMenuItem {
const tagIcon = html`
<div style="display: flex; align-items: center; justify-content: center;">
<div
style="border-radius: 50%; height: 8px; width: 8px; margin: 4px; background-color: ${tag.color};"
></div>
</div>
`;
return {
key: tag.id,
name: html`${unsafeHTML(tag.title)}`,
icon: tagIcon,
action: async () => {
await action(tag);
},
};
}
}