mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
chore: bump bs (#8227)
This commit is contained in:
@@ -15,14 +15,14 @@
|
||||
"@affine/graphql": "workspace:*",
|
||||
"@affine/i18n": "workspace:*",
|
||||
"@affine/templates": "workspace:*",
|
||||
"@blocksuite/affine-block-surface": "0.17.8",
|
||||
"@blocksuite/block-std": "0.17.8",
|
||||
"@blocksuite/blocks": "0.17.8",
|
||||
"@blocksuite/global": "0.17.8",
|
||||
"@blocksuite/affine-block-surface": "0.17.9",
|
||||
"@blocksuite/block-std": "0.17.9",
|
||||
"@blocksuite/blocks": "0.17.9",
|
||||
"@blocksuite/global": "0.17.9",
|
||||
"@blocksuite/icons": "2.1.67",
|
||||
"@blocksuite/inline": "0.17.8",
|
||||
"@blocksuite/presets": "0.17.8",
|
||||
"@blocksuite/store": "0.17.8",
|
||||
"@blocksuite/inline": "0.17.9",
|
||||
"@blocksuite/presets": "0.17.9",
|
||||
"@blocksuite/store": "0.17.9",
|
||||
"@dnd-kit/core": "^6.1.0",
|
||||
"@dnd-kit/modifiers": "^7.0.0",
|
||||
"@dnd-kit/sortable": "^8.0.0",
|
||||
|
||||
@@ -4,17 +4,21 @@ import type {
|
||||
EditorHost,
|
||||
TextSelection,
|
||||
} from '@blocksuite/block-std';
|
||||
import type {
|
||||
DocMode,
|
||||
EdgelessRootService,
|
||||
ImageSelection,
|
||||
RootService,
|
||||
} from '@blocksuite/blocks';
|
||||
import {
|
||||
type DocMode,
|
||||
BlocksUtils,
|
||||
DocModeProvider,
|
||||
type EdgelessRootService,
|
||||
EditPropsStore,
|
||||
type ImageSelection,
|
||||
NoteDisplayMode,
|
||||
NotificationProvider,
|
||||
type PageRootService,
|
||||
RefNodeSlotsProvider,
|
||||
TelemetryProvider,
|
||||
} from '@blocksuite/blocks';
|
||||
import { BlocksUtils, NoteDisplayMode } from '@blocksuite/blocks';
|
||||
import {
|
||||
Bound,
|
||||
getElementsBound,
|
||||
@@ -110,10 +114,7 @@ export async function constructRootChatBlockMessages(
|
||||
return constructUserInfoWithMessages(forkMessages, userInfo);
|
||||
}
|
||||
|
||||
function getViewportCenter(
|
||||
mode: DocMode,
|
||||
rootService: PageRootService | EdgelessRootService
|
||||
) {
|
||||
function getViewportCenter(mode: DocMode, rootService: RootService) {
|
||||
const center = { x: 400, y: 50 };
|
||||
if (mode === 'page') {
|
||||
const viewport = rootService.std.get(EditPropsStore).getStorage('viewport');
|
||||
@@ -310,7 +311,7 @@ const SAVE_CHAT_TO_BLOCK_ACTION: ChatAction = {
|
||||
const curMode = docModeService.getEditorMode() || 'page';
|
||||
const viewportCenter = getViewportCenter(
|
||||
curMode,
|
||||
rootService as PageRootService
|
||||
rootService as RootService
|
||||
);
|
||||
const newBlockIndex = layer.generateIndex('affine:embed-ai-chat');
|
||||
// If current mode is not edgeless, switch to edgeless mode first
|
||||
@@ -430,7 +431,7 @@ const CREATE_AS_DOC = {
|
||||
newDoc.addBlock('affine:surface', {}, rootId);
|
||||
const noteId = newDoc.addBlock('affine:note', {}, rootId);
|
||||
|
||||
host.std.getService('affine:page')?.slots.docLinkClicked.emit({
|
||||
host.std.getOptional(RefNodeSlotsProvider)?.docLinkClicked.emit({
|
||||
pageId: newDoc.id,
|
||||
});
|
||||
let complete = false;
|
||||
|
||||
@@ -42,7 +42,7 @@ const processTypeToPromptName = new Map(
|
||||
})
|
||||
);
|
||||
|
||||
function setupAIProvider() {
|
||||
export function setupAIProvider() {
|
||||
// a single workspace should have only a single chat session
|
||||
// user-id:workspace-id:doc-id -> chat session id
|
||||
const chatSessions = new Map<string, Promise<string>>();
|
||||
@@ -490,5 +490,3 @@ Could you make a new website based on these notes and send back just the html fi
|
||||
|
||||
setupTracker();
|
||||
}
|
||||
|
||||
setupAIProvider();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { ReferenceInfo } from '@blocksuite/affine-model';
|
||||
import type { DocMode } from '@blocksuite/blocks';
|
||||
import type {
|
||||
AffineEditorContainer,
|
||||
@@ -21,21 +20,6 @@ import {
|
||||
import { BlocksuiteDocEditor, BlocksuiteEdgelessEditor } from './lit-adaper';
|
||||
import * as styles from './styles.css';
|
||||
|
||||
// copy forwardSlot from blocksuite, but it seems we need to dispose the pipe
|
||||
// after the component is unmounted right?
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function forwardSlot<T extends Record<string, Slot<any>>>(
|
||||
from: T,
|
||||
to: Partial<T>
|
||||
) {
|
||||
Object.entries(from).forEach(([key, slot]) => {
|
||||
const target = to[key];
|
||||
if (target) {
|
||||
slot.pipe(target);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
interface BlocksuiteEditorContainerProps {
|
||||
page: Doc;
|
||||
mode: DocMode;
|
||||
@@ -65,38 +49,15 @@ export const BlocksuiteEditorContainer = forwardRef<
|
||||
|
||||
const slots: BlocksuiteEditorContainerRef['slots'] = useMemo(() => {
|
||||
return {
|
||||
docLinkClicked: new Slot<ReferenceInfo>(),
|
||||
editorModeSwitched: new Slot(),
|
||||
docUpdated: new Slot(),
|
||||
tagClicked: new Slot(),
|
||||
};
|
||||
}, []);
|
||||
|
||||
// forward the slot to the webcomponent
|
||||
useLayoutEffect(() => {
|
||||
requestAnimationFrame(() => {
|
||||
const docPage = rootRef.current?.querySelector('affine-page-root');
|
||||
const edgelessPage = rootRef.current?.querySelector(
|
||||
'affine-edgeless-root'
|
||||
);
|
||||
if (docPage) {
|
||||
forwardSlot(docPage.slots, slots);
|
||||
}
|
||||
|
||||
if (edgelessPage) {
|
||||
forwardSlot(edgelessPage.slots, slots);
|
||||
}
|
||||
});
|
||||
}, [page, slots]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
slots.docUpdated.emit({ newDocId: page.id });
|
||||
}, [page, slots.docUpdated]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
slots.editorModeSwitched.emit(mode);
|
||||
}, [mode, slots.editorModeSwitched]);
|
||||
|
||||
/**
|
||||
* mimic an AffineEditorContainer using proxy
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
export * from './blocksuite-editor';
|
||||
export { getFontConfigExtension } from './specs/font-extension';
|
||||
|
||||
import './ai/setup-provider';
|
||||
import './specs/preview';
|
||||
import { effects as blocksEffects } from '@blocksuite/blocks/effects';
|
||||
import { effects as presetsEffects } from '@blocksuite/presets/effects';
|
||||
|
||||
import { setupAIProvider } from './ai/setup-provider';
|
||||
import { effects as patchEffects } from './specs/preview';
|
||||
|
||||
blocksEffects();
|
||||
presetsEffects();
|
||||
patchEffects();
|
||||
setupAIProvider();
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
patchEdgelessClipboard,
|
||||
patchForSharedPage,
|
||||
patchNotificationService,
|
||||
patchParseDocUrlExtension,
|
||||
patchPeekViewService,
|
||||
patchQuickSearchService,
|
||||
patchReferenceRenderer,
|
||||
@@ -118,9 +119,8 @@ const usePatchSpecs = (page: Doc, shared: boolean, mode: DocMode) => {
|
||||
patched = patched.concat(patchNotificationService(confirmModal));
|
||||
patched = patched.concat(patchPeekViewService(peekViewService));
|
||||
patched = patched.concat(patchEdgelessClipboard());
|
||||
if (!page.readonly) {
|
||||
patched = patched.concat(patchQuickSearchService(framework));
|
||||
}
|
||||
patched = patched.concat(patchParseDocUrlExtension(framework));
|
||||
patched = patched.concat(patchQuickSearchService(framework));
|
||||
if (shared) {
|
||||
patched = patched.concat(patchForSharedPage());
|
||||
}
|
||||
@@ -134,7 +134,6 @@ const usePatchSpecs = (page: Doc, shared: boolean, mode: DocMode) => {
|
||||
docsService,
|
||||
editorService,
|
||||
framework,
|
||||
page.readonly,
|
||||
peekViewService,
|
||||
reactToLit,
|
||||
referenceRenderer,
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
LatexBlockSpec,
|
||||
ListBlockSpec,
|
||||
ParagraphBlockSpec,
|
||||
RefNodeSlotsExtension,
|
||||
RichTextExtensions,
|
||||
} from '@blocksuite/blocks';
|
||||
import { AIChatBlockSpec } from '@blocksuite/presets';
|
||||
@@ -29,6 +30,7 @@ import { AIChatBlockSpec } from '@blocksuite/presets';
|
||||
import { CustomAttachmentBlockSpec } from './custom/attachment-block';
|
||||
|
||||
const CommonBlockSpecs: ExtensionType[] = [
|
||||
RefNodeSlotsExtension(),
|
||||
EditPropsStore,
|
||||
RichTextExtensions,
|
||||
LatexBlockSpec,
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
type useConfirmModal,
|
||||
} from '@affine/component';
|
||||
import { track } from '@affine/core/mixpanel';
|
||||
import { DocsSearchService } from '@affine/core/modules/docs-search';
|
||||
import type { EditorService } from '@affine/core/modules/editor';
|
||||
import { resolveLinkToDoc } from '@affine/core/modules/navigation';
|
||||
import type { PeekViewService } from '@affine/core/modules/peek-view';
|
||||
@@ -20,6 +19,7 @@ import {
|
||||
QuickSearchService,
|
||||
RecentDocsQuickSearchSession,
|
||||
} from '@affine/core/modules/quicksearch';
|
||||
import { ExternalLinksQuickSearchSession } from '@affine/core/modules/quicksearch/impls/external-links';
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import {
|
||||
type BlockService,
|
||||
@@ -43,6 +43,7 @@ import {
|
||||
EmbedLinkedDocBlockComponent,
|
||||
EmbedOptionProvider,
|
||||
NotificationExtension,
|
||||
ParseDocUrlExtension,
|
||||
PeekViewExtension,
|
||||
QuickSearchExtension,
|
||||
QuickSearchProvider,
|
||||
@@ -54,6 +55,7 @@ import {
|
||||
type DocService,
|
||||
DocsService,
|
||||
type FrameworkProvider,
|
||||
WorkspaceService,
|
||||
} from '@toeverything/infra';
|
||||
import { type TemplateResult } from 'lit';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
@@ -282,109 +284,84 @@ export function patchDocModeService(
|
||||
|
||||
export function patchQuickSearchService(framework: FrameworkProvider) {
|
||||
const QuickSearch = QuickSearchExtension({
|
||||
async searchDoc(options) {
|
||||
async openQuickSearch() {
|
||||
let searchResult: QuickSearchResult = null;
|
||||
if (options.skipSelection) {
|
||||
const query = options.userInput;
|
||||
if (!query) {
|
||||
logger.error('No user input provided');
|
||||
} else {
|
||||
const resolvedDoc = resolveLinkToDoc(query);
|
||||
if (resolvedDoc) {
|
||||
searchResult = {
|
||||
docId: resolvedDoc.docId,
|
||||
};
|
||||
} else if (
|
||||
query.startsWith('http://') ||
|
||||
query.startsWith('https://')
|
||||
) {
|
||||
searchResult = {
|
||||
userInput: query,
|
||||
};
|
||||
} else {
|
||||
const searchedDoc = (
|
||||
await framework.get(DocsSearchService).search(query)
|
||||
).at(0);
|
||||
if (searchedDoc) {
|
||||
searchResult = {
|
||||
docId: searchedDoc.docId,
|
||||
};
|
||||
searchResult = await new Promise(resolve =>
|
||||
framework.get(QuickSearchService).quickSearch.show(
|
||||
[
|
||||
framework.get(RecentDocsQuickSearchSession),
|
||||
framework.get(CreationQuickSearchSession),
|
||||
framework.get(DocsQuickSearchSession),
|
||||
framework.get(LinksQuickSearchSession),
|
||||
framework.get(ExternalLinksQuickSearchSession),
|
||||
],
|
||||
result => {
|
||||
if (result === null) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
searchResult = await new Promise(resolve =>
|
||||
framework.get(QuickSearchService).quickSearch.show(
|
||||
[
|
||||
framework.get(RecentDocsQuickSearchSession),
|
||||
framework.get(CreationQuickSearchSession),
|
||||
framework.get(DocsQuickSearchSession),
|
||||
framework.get(LinksQuickSearchSession),
|
||||
],
|
||||
result => {
|
||||
if (result === null) {
|
||||
resolve(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.source === 'docs') {
|
||||
resolve({
|
||||
docId: result.payload.docId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (result.source === 'docs') {
|
||||
resolve({
|
||||
docId: result.payload.docId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.source === 'recent-doc') {
|
||||
resolve({
|
||||
docId: result.payload.docId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (result.source === 'recent-doc') {
|
||||
resolve({
|
||||
docId: result.payload.docId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.source === 'link') {
|
||||
if (result.payload.external) {
|
||||
const userInput = result.payload.external.url;
|
||||
resolve({ userInput });
|
||||
return;
|
||||
}
|
||||
if (result.source === 'link') {
|
||||
const { docId, blockIds, elementIds, mode } = result.payload;
|
||||
resolve({
|
||||
docId,
|
||||
params: {
|
||||
blockIds,
|
||||
elementIds,
|
||||
mode,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.payload.internal) {
|
||||
const { docId, params } = result.payload.internal;
|
||||
resolve({ docId, params });
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (result.source === 'external-link') {
|
||||
const externalUrl = result.payload.url;
|
||||
resolve({ externalUrl });
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.source === 'creation') {
|
||||
const docsService = framework.get(DocsService);
|
||||
const mode =
|
||||
result.id === 'creation:create-edgeless'
|
||||
? 'edgeless'
|
||||
: 'page';
|
||||
const newDoc = docsService.createDoc({
|
||||
primaryMode: mode,
|
||||
title: result.payload.title,
|
||||
});
|
||||
if (result.source === 'creation') {
|
||||
const docsService = framework.get(DocsService);
|
||||
const mode =
|
||||
result.id === 'creation:create-edgeless' ? 'edgeless' : 'page';
|
||||
const newDoc = docsService.createDoc({
|
||||
primaryMode: mode,
|
||||
title: result.payload.title,
|
||||
});
|
||||
track.doc.editor.quickSearch.createDoc({
|
||||
mode,
|
||||
});
|
||||
|
||||
resolve({
|
||||
docId: newDoc.id,
|
||||
isNewDoc: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
resolve({
|
||||
docId: newDoc.id,
|
||||
});
|
||||
return;
|
||||
}
|
||||
},
|
||||
{
|
||||
label: {
|
||||
key: 'com.affine.cmdk.insert-links',
|
||||
},
|
||||
{
|
||||
defaultQuery: options.userInput,
|
||||
label: {
|
||||
key: 'com.affine.cmdk.insert-links',
|
||||
},
|
||||
placeholder: {
|
||||
key: 'com.affine.cmdk.docs.placeholder',
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
placeholder: {
|
||||
key: 'com.affine.cmdk.docs.placeholder',
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return searchResult;
|
||||
},
|
||||
@@ -408,7 +385,7 @@ export function patchQuickSearchService(framework: FrameworkProvider) {
|
||||
if (!quickSearchService)
|
||||
return oldAction({ model, rootComponent });
|
||||
|
||||
const result = await quickSearchService.searchDoc({});
|
||||
const result = await quickSearchService.openQuickSearch();
|
||||
if (result === null) return;
|
||||
|
||||
if ('docId' in result) {
|
||||
@@ -424,28 +401,23 @@ export function patchQuickSearchService(framework: FrameworkProvider) {
|
||||
pageId: linkedDoc.id,
|
||||
};
|
||||
|
||||
if (!result.isNewDoc && result.params) {
|
||||
if (result.params) {
|
||||
props.params = result.params;
|
||||
}
|
||||
|
||||
host.doc.addSiblingBlocks(model, [props]);
|
||||
|
||||
if (result.isNewDoc) {
|
||||
track.doc.editor.slashMenu.createDoc({ control: 'linkDoc' });
|
||||
track.doc.editor.slashMenu.linkDoc({ control: 'createDoc' });
|
||||
} else {
|
||||
track.doc.editor.slashMenu.linkDoc({ control: 'linkDoc' });
|
||||
}
|
||||
} else if ('userInput' in result) {
|
||||
track.doc.editor.slashMenu.linkDoc({ control: 'linkDoc' });
|
||||
} else if (result.externalUrl) {
|
||||
const embedOptions = std
|
||||
.get(EmbedOptionProvider)
|
||||
.getEmbedBlockOptions(result.userInput);
|
||||
.getEmbedBlockOptions(result.externalUrl);
|
||||
if (!embedOptions) return;
|
||||
|
||||
host.doc.addSiblingBlocks(model, [
|
||||
{
|
||||
flavour: embedOptions.flavour,
|
||||
url: result.userInput,
|
||||
url: result.externalUrl,
|
||||
},
|
||||
]);
|
||||
}
|
||||
@@ -458,6 +430,25 @@ export function patchQuickSearchService(framework: FrameworkProvider) {
|
||||
return [QuickSearch, SlashMenuQuickSearchExtension];
|
||||
}
|
||||
|
||||
export function patchParseDocUrlExtension(framework: FrameworkProvider) {
|
||||
const workspaceService = framework.get(WorkspaceService);
|
||||
const ParseDocUrl = ParseDocUrlExtension({
|
||||
parseDocUrl(url) {
|
||||
const info = resolveLinkToDoc(url);
|
||||
if (!info || info.workspaceId !== workspaceService.workspace.id) return;
|
||||
|
||||
return {
|
||||
docId: info.docId,
|
||||
blockIds: info.blockIds,
|
||||
elementIds: info.elementIds,
|
||||
mode: info.mode,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
return [ParseDocUrl];
|
||||
}
|
||||
|
||||
export function patchEdgelessClipboard() {
|
||||
class EdgelessClipboardWatcher extends BlockServiceWatcher {
|
||||
static override readonly flavour = 'affine:page';
|
||||
|
||||
@@ -9,5 +9,7 @@ function patchPreviewSpec(id: string, specs: ExtensionType[]) {
|
||||
specProvider.extendSpec(id, specs);
|
||||
}
|
||||
|
||||
// Patch edgeless preview spec for blocksuite surface-ref and embed-synced-doc
|
||||
patchPreviewSpec('edgeless:preview', CustomSpecs);
|
||||
export function effects() {
|
||||
// Patch edgeless preview spec for blocksuite surface-ref and embed-synced-doc
|
||||
patchPreviewSpec('edgeless:preview', CustomSpecs);
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
import { track } from '@affine/core/mixpanel';
|
||||
import { EditorService } from '@affine/core/modules/editor';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import type { PageRootService } from '@blocksuite/blocks';
|
||||
import {
|
||||
ExportManager,
|
||||
HtmlTransformer,
|
||||
MarkdownTransformer,
|
||||
printToPdf,
|
||||
@@ -34,27 +34,22 @@ async function exportHandler({
|
||||
editorContainer,
|
||||
}: ExportHandlerOptions) {
|
||||
const editorRoot = document.querySelector('editor-host');
|
||||
let pageService: PageRootService | null = null;
|
||||
if (editorRoot) {
|
||||
pageService = editorRoot.std.getService<PageRootService>('affine:page');
|
||||
}
|
||||
track.$.sharePanel.$.export({
|
||||
type,
|
||||
});
|
||||
switch (type) {
|
||||
case 'html':
|
||||
await HtmlTransformer.exportDoc(page);
|
||||
break;
|
||||
return;
|
||||
case 'markdown':
|
||||
await MarkdownTransformer.exportDoc(page);
|
||||
break;
|
||||
return;
|
||||
case 'pdf':
|
||||
await printToPdf(editorContainer);
|
||||
return;
|
||||
case 'png': {
|
||||
if (!pageService) return;
|
||||
await pageService.exportManager.exportPng();
|
||||
break;
|
||||
await editorRoot?.std.get(ExportManager).exportPng();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,6 +264,7 @@ const PageEvents = {
|
||||
editor: {
|
||||
slashMenu: ['linkDoc', 'createDoc'],
|
||||
atMenu: ['linkDoc'],
|
||||
quickSearch: ['createDoc'],
|
||||
formatToolbar: ['bold'],
|
||||
pageRef: ['navigate'],
|
||||
toolbar: ['copyBlockToLink'],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { DocMode, EdgelessRootService } from '@blocksuite/blocks';
|
||||
import type { InlineEditor } from '@blocksuite/inline/inline-editor';
|
||||
import type { InlineEditor } from '@blocksuite/inline';
|
||||
import type { AffineEditorContainer, DocTitle } from '@blocksuite/presets';
|
||||
import type { DocService, WorkspaceService } from '@toeverything/infra';
|
||||
import { Entity, LiveData } from '@toeverything/infra';
|
||||
|
||||
@@ -7,7 +7,11 @@ import { EditorOutlineViewer } from '@affine/core/components/blocksuite/outline-
|
||||
import { EditorService } from '@affine/core/modules/editor';
|
||||
import { PageNotFound } from '@affine/core/pages/404';
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import type { DocMode, EdgelessRootService } from '@blocksuite/blocks';
|
||||
import {
|
||||
type DocMode,
|
||||
type EdgelessRootService,
|
||||
RefNodeSlotsProvider,
|
||||
} from '@blocksuite/blocks';
|
||||
import { Bound, DisposableGroup } from '@blocksuite/global/utils';
|
||||
import type { AffineEditorContainer } from '@blocksuite/presets';
|
||||
import {
|
||||
@@ -87,13 +91,12 @@ function DocPeekPreviewEditor({
|
||||
return;
|
||||
}
|
||||
const disposableGroup = new DisposableGroup();
|
||||
const rootService = editorContainer.host.std.getService('affine:page');
|
||||
if (!rootService) {
|
||||
return;
|
||||
}
|
||||
const refNodeSlots =
|
||||
editorContainer.host.std.getOptional(RefNodeSlotsProvider);
|
||||
if (!refNodeSlots) return;
|
||||
// doc change event inside peek view should be handled by peek view
|
||||
disposableGroup.add(
|
||||
rootService.slots.docLinkClicked.on(options => {
|
||||
refNodeSlots.docLinkClicked.on(options => {
|
||||
peekView
|
||||
.open({
|
||||
type: 'doc',
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import { LinkIcon } from '@blocksuite/icons/rc';
|
||||
import type { WorkspaceService } from '@toeverything/infra';
|
||||
import { Entity, LiveData } from '@toeverything/infra';
|
||||
|
||||
import { resolveLinkToDoc } from '../../navigation';
|
||||
import type { QuickSearchSession } from '../providers/quick-search-provider';
|
||||
import type { QuickSearchItem } from '../types/item';
|
||||
|
||||
type ExternalLinkPayload = {
|
||||
url: string;
|
||||
};
|
||||
|
||||
export class ExternalLinksQuickSearchSession
|
||||
extends Entity
|
||||
implements QuickSearchSession<'external-link', ExternalLinkPayload>
|
||||
{
|
||||
constructor(private readonly workspaceService: WorkspaceService) {
|
||||
super();
|
||||
}
|
||||
|
||||
query$ = new LiveData('');
|
||||
|
||||
items$ = LiveData.computed(get => {
|
||||
const query = get(this.query$);
|
||||
if (!query) return [];
|
||||
|
||||
const isLink = query.startsWith('http://') || query.startsWith('https://');
|
||||
if (!isLink) return [];
|
||||
|
||||
const resolvedDoc = resolveLinkToDoc(query);
|
||||
if (
|
||||
resolvedDoc &&
|
||||
resolvedDoc.workspaceId === this.workspaceService.workspace.id
|
||||
) {
|
||||
// is doc url
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
id: 'external-link:' + query,
|
||||
source: 'external-link',
|
||||
icon: LinkIcon,
|
||||
label: {
|
||||
key: 'com.affine.cmdk.affine.insert-link',
|
||||
},
|
||||
payload: { url: query },
|
||||
} as QuickSearchItem<'external-link', ExternalLinkPayload>,
|
||||
];
|
||||
});
|
||||
|
||||
query(query: string) {
|
||||
this.query$.next(query);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { ReferenceParams } from '@blocksuite/blocks';
|
||||
import { BlockLinkIcon, LinkIcon } from '@blocksuite/icons/rc';
|
||||
import type { DocsService } from '@toeverything/infra';
|
||||
import type { DocMode } from '@blocksuite/blocks';
|
||||
import { BlockLinkIcon, EdgelessIcon, PageIcon } from '@blocksuite/icons/rc';
|
||||
import type { DocsService, WorkspaceService } from '@toeverything/infra';
|
||||
import { Entity, LiveData } from '@toeverything/infra';
|
||||
import { isEmpty, pick, truncate } from 'lodash-es';
|
||||
import { truncate } from 'lodash-es';
|
||||
|
||||
import { resolveLinkToDoc } from '../../navigation';
|
||||
import type { QuickSearchSession } from '../providers/quick-search-provider';
|
||||
@@ -10,16 +10,10 @@ import type { DocDisplayMetaService } from '../services/doc-display-meta';
|
||||
import type { QuickSearchItem } from '../types/item';
|
||||
|
||||
type LinkPayload = {
|
||||
internal?: {
|
||||
docId: string;
|
||||
title?: string;
|
||||
blockId?: string;
|
||||
blockContent?: string;
|
||||
params?: ReferenceParams;
|
||||
};
|
||||
external?: {
|
||||
url: string;
|
||||
};
|
||||
docId: string;
|
||||
blockIds?: string[];
|
||||
elementIds?: string[];
|
||||
mode?: DocMode;
|
||||
};
|
||||
|
||||
export class LinksQuickSearchSession
|
||||
@@ -27,6 +21,7 @@ export class LinksQuickSearchSession
|
||||
implements QuickSearchSession<'link', LinkPayload>
|
||||
{
|
||||
constructor(
|
||||
private readonly workspaceService: WorkspaceService,
|
||||
private readonly docsService: DocsService,
|
||||
private readonly docDisplayMetaService: DocDisplayMetaService
|
||||
) {
|
||||
@@ -43,44 +38,25 @@ export class LinksQuickSearchSession
|
||||
if (!isLink) return [];
|
||||
|
||||
const resolvedDoc = resolveLinkToDoc(query);
|
||||
if (!resolvedDoc) {
|
||||
return [
|
||||
{
|
||||
id: 'link',
|
||||
source: 'link',
|
||||
icon: LinkIcon,
|
||||
label: {
|
||||
key: 'com.affine.cmdk.affine.insert-link',
|
||||
},
|
||||
payload: { external: { url: query } },
|
||||
} as QuickSearchItem<'link', LinkPayload>,
|
||||
];
|
||||
if (
|
||||
!resolvedDoc ||
|
||||
resolvedDoc.workspaceId !== this.workspaceService.workspace.id
|
||||
) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const docId = resolvedDoc.docId;
|
||||
const doc = this.docsService.list.doc$(docId).value;
|
||||
if (!doc || get(doc.trash$)) return [];
|
||||
|
||||
const params = pick(resolvedDoc, ['mode', 'blockIds', 'elementIds']);
|
||||
const { title, icon, updatedDate } =
|
||||
this.docDisplayMetaService.getDocDisplayMeta(doc);
|
||||
const blockId = params?.blockIds?.[0];
|
||||
const linkToNode = Boolean(blockId);
|
||||
const linkToNode = resolvedDoc.blockIds || resolvedDoc.elementIds;
|
||||
const score = 100;
|
||||
const internal = {
|
||||
docId,
|
||||
score,
|
||||
blockId,
|
||||
blockContent: '',
|
||||
};
|
||||
|
||||
if (linkToNode && !isEmpty(params)) {
|
||||
Object.assign(internal, { params });
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
id: ['doc', doc.id, linkToNode ? blockId : ''].join(':'),
|
||||
id: 'links:doc:' + doc.id,
|
||||
source: 'link',
|
||||
group: {
|
||||
id: 'docs',
|
||||
@@ -94,9 +70,20 @@ export class LinksQuickSearchSession
|
||||
title: title,
|
||||
},
|
||||
score,
|
||||
icon: linkToNode ? BlockLinkIcon : icon,
|
||||
icon: linkToNode
|
||||
? BlockLinkIcon
|
||||
: resolvedDoc.mode === 'page'
|
||||
? PageIcon
|
||||
: resolvedDoc.mode === 'edgeless'
|
||||
? EdgelessIcon
|
||||
: icon,
|
||||
timestamp: updatedDate,
|
||||
payload: { internal },
|
||||
payload: {
|
||||
docId,
|
||||
blockIds: resolvedDoc.blockIds,
|
||||
elementIds: resolvedDoc.elementIds,
|
||||
mode: resolvedDoc.mode,
|
||||
},
|
||||
} as QuickSearchItem<'link', LinkPayload>,
|
||||
];
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
GlobalContextService,
|
||||
WorkspaceLocalState,
|
||||
WorkspaceScope,
|
||||
WorkspaceService,
|
||||
} from '@toeverything/infra';
|
||||
|
||||
import { CollectionService } from '../collection';
|
||||
@@ -16,6 +17,7 @@ import { CollectionsQuickSearchSession } from './impls/collections';
|
||||
import { CommandsQuickSearchSession } from './impls/commands';
|
||||
import { CreationQuickSearchSession } from './impls/creation';
|
||||
import { DocsQuickSearchSession } from './impls/docs';
|
||||
import { ExternalLinksQuickSearchSession } from './impls/external-links';
|
||||
import { LinksQuickSearchSession } from './impls/links';
|
||||
import { RecentDocsQuickSearchSession } from './impls/recent-docs';
|
||||
import { TagsQuickSearchSession } from './impls/tags';
|
||||
@@ -30,6 +32,7 @@ export { CollectionsQuickSearchSession } from './impls/collections';
|
||||
export { CommandsQuickSearchSession } from './impls/commands';
|
||||
export { CreationQuickSearchSession } from './impls/creation';
|
||||
export { DocsQuickSearchSession } from './impls/docs';
|
||||
export { ExternalLinksQuickSearchSession } from './impls/external-links';
|
||||
export { LinksQuickSearchSession } from './impls/links';
|
||||
export { RecentDocsQuickSearchSession } from './impls/recent-docs';
|
||||
export { TagsQuickSearchSession } from './impls/tags';
|
||||
@@ -55,7 +58,12 @@ export function configureQuickSearchModule(framework: Framework) {
|
||||
DocsService,
|
||||
DocDisplayMetaService,
|
||||
])
|
||||
.entity(LinksQuickSearchSession, [DocsService, DocDisplayMetaService])
|
||||
.entity(LinksQuickSearchSession, [
|
||||
WorkspaceService,
|
||||
DocsService,
|
||||
DocDisplayMetaService,
|
||||
])
|
||||
.entity(ExternalLinksQuickSearchSession, [WorkspaceService])
|
||||
.entity(CreationQuickSearchSession)
|
||||
.entity(CollectionsQuickSearchSession, [CollectionService])
|
||||
.entity(TagsQuickSearchSession, [TagService])
|
||||
|
||||
@@ -48,13 +48,13 @@ export class CMDKQuickSearchService extends Service {
|
||||
}
|
||||
|
||||
if (result.source === 'link') {
|
||||
if (result.payload.internal) {
|
||||
const { docId, params } = result.payload.internal;
|
||||
this.workbenchService.workbench.openDoc({
|
||||
docId,
|
||||
...params,
|
||||
});
|
||||
}
|
||||
const { docId, blockIds, elementIds, mode } = result.payload;
|
||||
this.workbenchService.workbench.openDoc({
|
||||
docId,
|
||||
blockIds,
|
||||
elementIds,
|
||||
mode,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useDocMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta';
|
||||
import { EditorService } from '@affine/core/modules/editor';
|
||||
import { RecentDocsService } from '@affine/core/modules/quicksearch';
|
||||
import { ViewService } from '@affine/core/modules/workbench/services/view';
|
||||
import type { PageRootService } from '@blocksuite/blocks';
|
||||
import { RefNodeSlotsProvider } from '@blocksuite/blocks';
|
||||
import { DisposableGroup } from '@blocksuite/global/utils';
|
||||
import { AiIcon, FrameIcon, TocIcon, TodayIcon } from '@blocksuite/icons/rc';
|
||||
import { type AffineEditorContainer } from '@blocksuite/presets';
|
||||
@@ -164,26 +164,28 @@ const DetailPageImpl = memo(function DetailPageImpl() {
|
||||
// blocksuite editor host
|
||||
const editorHost = editorContainer.host;
|
||||
|
||||
const pageService =
|
||||
editorHost?.std.getService<PageRootService>('affine:page');
|
||||
const std = editorHost?.std;
|
||||
const disposable = new DisposableGroup();
|
||||
if (pageService) {
|
||||
disposable.add(
|
||||
pageService.slots.docLinkClicked.on(({ pageId, params }) => {
|
||||
if (params) {
|
||||
const { mode, blockIds, elementIds } = params;
|
||||
return jumpToPageBlock(
|
||||
docCollection.id,
|
||||
pageId,
|
||||
mode,
|
||||
blockIds,
|
||||
elementIds
|
||||
);
|
||||
}
|
||||
if (std) {
|
||||
const refNodeSlots = std.getOptional(RefNodeSlotsProvider);
|
||||
if (refNodeSlots) {
|
||||
disposable.add(
|
||||
refNodeSlots.docLinkClicked.on(({ pageId, params }) => {
|
||||
if (params) {
|
||||
const { mode, blockIds, elementIds } = params;
|
||||
return jumpToPageBlock(
|
||||
docCollection.id,
|
||||
pageId,
|
||||
mode,
|
||||
blockIds,
|
||||
elementIds
|
||||
);
|
||||
}
|
||||
|
||||
return openPage(docCollection.id, pageId);
|
||||
})
|
||||
);
|
||||
return openPage(docCollection.id, pageId);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
editor.setEditorContainer(editorContainer);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ChatPanel } from '@affine/core/blocksuite/presets/ai';
|
||||
import { DocModeProvider } from '@blocksuite/blocks';
|
||||
import { DocModeProvider, RefNodeSlotsProvider } from '@blocksuite/blocks';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
import type { AffineEditorContainer } from '@blocksuite/presets';
|
||||
import { forwardRef, useCallback, useEffect, useRef } from 'react';
|
||||
@@ -46,19 +46,21 @@ export const EditorChatPanel = forwardRef(function EditorChatPanel(
|
||||
const pageService = editor.host?.std.getService('affine:page');
|
||||
if (!pageService) return;
|
||||
const docModeService = editor.host?.std.get(DocModeProvider);
|
||||
if (!docModeService) return;
|
||||
const refNodeService = editor.host?.std.getOptional(RefNodeSlotsProvider);
|
||||
|
||||
const disposable = [
|
||||
pageService.slots.docLinkClicked.on(() => {
|
||||
(chatPanelRef.current as ChatPanel).doc = editor.doc;
|
||||
}),
|
||||
docModeService.onPrimaryModeChange(() => {
|
||||
if (!editor.host) return;
|
||||
(chatPanelRef.current as ChatPanel).host = editor.host;
|
||||
}, editor.doc.id),
|
||||
refNodeService &&
|
||||
refNodeService.docLinkClicked.on(() => {
|
||||
(chatPanelRef.current as ChatPanel).doc = editor.doc;
|
||||
}),
|
||||
docModeService &&
|
||||
docModeService.onPrimaryModeChange(() => {
|
||||
if (!editor.host) return;
|
||||
(chatPanelRef.current as ChatPanel).host = editor.host;
|
||||
}, editor.doc.id),
|
||||
];
|
||||
|
||||
return () => disposable.forEach(d => d.dispose());
|
||||
return () => disposable.forEach(d => d?.dispose());
|
||||
}, [editor]);
|
||||
|
||||
if (!editor) {
|
||||
|
||||
Reference in New Issue
Block a user