mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-20 11:36:25 +08:00
chore: bump bs with new extension api (#8042)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { DocMode } from '@blocksuite/blocks';
|
||||
import { DocMode } from '@blocksuite/blocks';
|
||||
import type { AffineEditorContainer } from '@blocksuite/presets';
|
||||
import type { DocService, WorkspaceService } from '@toeverything/infra';
|
||||
import { Entity, LiveData } from '@toeverything/infra';
|
||||
@@ -18,7 +18,9 @@ export class Editor extends Entity<{ defaultMode: DocMode }> {
|
||||
readonly editorContainer$ = new LiveData<AffineEditorContainer | null>(null);
|
||||
|
||||
toggleMode() {
|
||||
this.mode$.next(this.mode$.value === 'edgeless' ? 'page' : 'edgeless');
|
||||
this.mode$.next(
|
||||
this.mode$.value === 'edgeless' ? DocMode.Page : DocMode.Edgeless
|
||||
);
|
||||
}
|
||||
|
||||
setMode(mode: DocMode) {
|
||||
|
||||
@@ -25,19 +25,19 @@ afterEach(() => {
|
||||
const testCases: [string, ReturnType<typeof resolveLinkToDoc>][] = [
|
||||
['http://example.com/', null],
|
||||
[
|
||||
'/workspace/48__RTCSwASvWZxyAk3Jw/-Uge-K6SYcAbcNYfQ5U-j#xxxx',
|
||||
'/workspace/48__RTCSwASvWZxyAk3Jw/-Uge-K6SYcAbcNYfQ5U-j?blockIds=xxxx',
|
||||
{
|
||||
workspaceId: '48__RTCSwASvWZxyAk3Jw',
|
||||
docId: '-Uge-K6SYcAbcNYfQ5U-j',
|
||||
blockId: 'xxxx',
|
||||
blockIds: ['xxxx'],
|
||||
},
|
||||
],
|
||||
[
|
||||
'http://affine.pro/workspace/48__RTCSwASvWZxyAk3Jw/-Uge-K6SYcAbcNYfQ5U-j#xxxx',
|
||||
'http://affine.pro/workspace/48__RTCSwASvWZxyAk3Jw/-Uge-K6SYcAbcNYfQ5U-j?blockIds=xxxx',
|
||||
{
|
||||
workspaceId: '48__RTCSwASvWZxyAk3Jw',
|
||||
docId: '-Uge-K6SYcAbcNYfQ5U-j',
|
||||
blockId: 'xxxx',
|
||||
blockIds: ['xxxx'],
|
||||
},
|
||||
],
|
||||
['http://affine.pro/workspace/48__RTCSwASvWZxyAk3Jw/all', null],
|
||||
@@ -45,19 +45,19 @@ const testCases: [string, ReturnType<typeof resolveLinkToDoc>][] = [
|
||||
['http://affine.pro/workspace/48__RTCSwASvWZxyAk3Jw/tag', null],
|
||||
['http://affine.pro/workspace/48__RTCSwASvWZxyAk3Jw/trash', null],
|
||||
[
|
||||
'file//./workspace/48__RTCSwASvWZxyAk3Jw/-Uge-K6SYcAbcNYfQ5U-j#xxxx',
|
||||
'file//./workspace/48__RTCSwASvWZxyAk3Jw/-Uge-K6SYcAbcNYfQ5U-j?blockIds=xxxx',
|
||||
{
|
||||
workspaceId: '48__RTCSwASvWZxyAk3Jw',
|
||||
docId: '-Uge-K6SYcAbcNYfQ5U-j',
|
||||
blockId: 'xxxx',
|
||||
blockIds: ['xxxx'],
|
||||
},
|
||||
],
|
||||
[
|
||||
'http//localhost:8000/workspace/48__RTCSwASvWZxyAk3Jw/-Uge-K6SYcAbcNYfQ5U-j#xxxx',
|
||||
'http//localhost:8000/workspace/48__RTCSwASvWZxyAk3Jw/-Uge-K6SYcAbcNYfQ5U-j?blockIds=xxxx',
|
||||
{
|
||||
workspaceId: '48__RTCSwASvWZxyAk3Jw',
|
||||
docId: '-Uge-K6SYcAbcNYfQ5U-j',
|
||||
blockId: 'xxxx',
|
||||
blockIds: ['xxxx'],
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import type { DocMode } from '@blocksuite/blocks';
|
||||
import queryString from 'query-string';
|
||||
|
||||
function maybeAffineOrigin(origin: string) {
|
||||
return (
|
||||
origin.startsWith('file://') ||
|
||||
@@ -73,9 +76,23 @@ const isRouteModulePath = (
|
||||
export const resolveLinkToDoc = (href: string) => {
|
||||
const meta = resolveRouteLinkMeta(href);
|
||||
if (!meta || meta.moduleName !== 'doc') return null;
|
||||
|
||||
const params: {
|
||||
mode?: DocMode;
|
||||
blockIds?: string[];
|
||||
elementIds?: string[];
|
||||
} = queryString.parse(meta.location.search, {
|
||||
arrayFormat: 'none',
|
||||
types: {
|
||||
mode: value => (value === 'edgeless' ? 'edgeless' : 'page') as DocMode,
|
||||
blockIds: value => value.split(','),
|
||||
elementIds: value => value.split(','),
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
workspaceId: meta.workspaceId,
|
||||
docId: meta.docId,
|
||||
blockId: meta.blockId,
|
||||
...params,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { BlockComponent, EditorHost } from '@blocksuite/block-std';
|
||||
import {
|
||||
AffineReference,
|
||||
type DocMode,
|
||||
type EmbedLinkedDocModel,
|
||||
type EmbedSyncedDocModel,
|
||||
type ImageBlockModel,
|
||||
@@ -9,7 +10,7 @@ import {
|
||||
} from '@blocksuite/blocks';
|
||||
import type { AIChatBlockModel } from '@blocksuite/presets';
|
||||
import type { BlockModel } from '@blocksuite/store';
|
||||
import { type DocMode, Entity, LiveData } from '@toeverything/infra';
|
||||
import { Entity, LiveData } from '@toeverything/infra';
|
||||
import type { TemplateResult } from 'lit';
|
||||
import { firstValueFrom, map, race } from 'rxjs';
|
||||
|
||||
@@ -21,20 +22,21 @@ export type PeekViewTarget =
|
||||
| BlockComponent
|
||||
| AffineReference
|
||||
| HTMLAnchorElement
|
||||
| { docId: string; blockId?: string };
|
||||
| { docId: string; blockIds?: string[] };
|
||||
|
||||
export interface DocPeekViewInfo {
|
||||
type: 'doc';
|
||||
docId: string;
|
||||
blockId?: string;
|
||||
mode?: DocMode;
|
||||
blockIds?: string[];
|
||||
elementIds?: string[];
|
||||
xywh?: `[${number},${number},${number},${number}]`;
|
||||
}
|
||||
|
||||
export type ImagePeekViewInfo = {
|
||||
type: 'image';
|
||||
docId: string;
|
||||
blockId: string;
|
||||
blockIds: [string];
|
||||
};
|
||||
|
||||
export type AIChatBlockPeekViewInfo = {
|
||||
@@ -58,15 +60,16 @@ export type ActivePeekView = {
|
||||
| AIChatBlockPeekViewInfo;
|
||||
};
|
||||
|
||||
const EMBED_DOC_FLAVOURS = [
|
||||
'affine:embed-linked-doc',
|
||||
'affine:embed-synced-doc',
|
||||
];
|
||||
|
||||
const isEmbedDocModel = (
|
||||
const isEmbedLinkedDocModel = (
|
||||
blockModel: BlockModel
|
||||
): blockModel is EmbedSyncedDocModel | EmbedLinkedDocModel => {
|
||||
return EMBED_DOC_FLAVOURS.includes(blockModel.flavour);
|
||||
): blockModel is EmbedLinkedDocModel => {
|
||||
return blockModel.flavour === 'affine:embed-linked-doc';
|
||||
};
|
||||
|
||||
const isEmbedSyncedDocModel = (
|
||||
blockModel: BlockModel
|
||||
): blockModel is EmbedSyncedDocModel => {
|
||||
return blockModel.flavour === 'affine:embed-synced-doc';
|
||||
};
|
||||
|
||||
const isImageBlockModel = (
|
||||
@@ -99,15 +102,26 @@ function resolvePeekInfoFromPeekTarget(
|
||||
}
|
||||
|
||||
if (peekTarget instanceof AffineReference) {
|
||||
if (peekTarget.refMeta) {
|
||||
return {
|
||||
const referenceInfo = peekTarget.referenceInfo;
|
||||
if (referenceInfo) {
|
||||
const { pageId: docId } = referenceInfo;
|
||||
const info: DocPeekViewInfo = {
|
||||
type: 'doc',
|
||||
docId: peekTarget.refMeta.id,
|
||||
docId,
|
||||
};
|
||||
Object.assign(info, referenceInfo.params);
|
||||
return info;
|
||||
}
|
||||
} else if ('model' in peekTarget) {
|
||||
const blockModel = peekTarget.model;
|
||||
if (isEmbedDocModel(blockModel)) {
|
||||
if (isEmbedLinkedDocModel(blockModel)) {
|
||||
const info: DocPeekViewInfo = {
|
||||
type: 'doc',
|
||||
docId: blockModel.pageId,
|
||||
};
|
||||
Object.assign(info, blockModel.params);
|
||||
return info;
|
||||
} else if (isEmbedSyncedDocModel(blockModel)) {
|
||||
return {
|
||||
type: 'doc',
|
||||
docId: blockModel.pageId,
|
||||
@@ -121,7 +135,7 @@ function resolvePeekInfoFromPeekTarget(
|
||||
return {
|
||||
type: 'doc',
|
||||
docId,
|
||||
mode: 'edgeless',
|
||||
mode: 'edgeless' as DocMode,
|
||||
xywh: refModel.xywh,
|
||||
};
|
||||
}
|
||||
@@ -129,7 +143,7 @@ function resolvePeekInfoFromPeekTarget(
|
||||
return {
|
||||
type: 'image',
|
||||
docId: blockModel.doc.id,
|
||||
blockId: blockModel.id,
|
||||
blockIds: [blockModel.id],
|
||||
};
|
||||
} else if (isAIChatBlockModel(blockModel)) {
|
||||
return {
|
||||
@@ -142,17 +156,28 @@ function resolvePeekInfoFromPeekTarget(
|
||||
} else if (peekTarget instanceof HTMLAnchorElement) {
|
||||
const maybeDoc = resolveLinkToDoc(peekTarget.href);
|
||||
if (maybeDoc) {
|
||||
return {
|
||||
const info: DocPeekViewInfo = {
|
||||
type: 'doc',
|
||||
docId: maybeDoc.docId,
|
||||
blockId: maybeDoc.blockId,
|
||||
};
|
||||
|
||||
if (maybeDoc.mode) {
|
||||
info.mode = maybeDoc.mode;
|
||||
}
|
||||
if (maybeDoc.blockIds?.length) {
|
||||
info.blockIds = maybeDoc.blockIds;
|
||||
}
|
||||
if (maybeDoc.elementIds?.length) {
|
||||
info.elementIds = maybeDoc.elementIds;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
} else if ('docId' in peekTarget) {
|
||||
return {
|
||||
type: 'doc',
|
||||
docId: peekTarget.docId,
|
||||
blockId: peekTarget.blockId,
|
||||
blockIds: peekTarget.blockIds,
|
||||
};
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -7,10 +7,9 @@ import { EditorOutlineViewer } from '@affine/core/components/blocksuite/outline-
|
||||
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
|
||||
import { PageNotFound } from '@affine/core/pages/404';
|
||||
import { DebugLogger } from '@affine/debug';
|
||||
import { type EdgelessRootService } from '@blocksuite/blocks';
|
||||
import { DocMode, type EdgelessRootService } from '@blocksuite/blocks';
|
||||
import { Bound, DisposableGroup } from '@blocksuite/global/utils';
|
||||
import type { AffineEditorContainer } from '@blocksuite/presets';
|
||||
import type { DocMode } from '@toeverything/infra';
|
||||
import { DocsService, FrameworkScope, useService } from '@toeverything/infra';
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
@@ -32,7 +31,7 @@ function fitViewport(
|
||||
}
|
||||
|
||||
const rootService =
|
||||
editor.host.std.spec.getService<EdgelessRootService>('affine:page');
|
||||
editor.host.std.getService<EdgelessRootService>('affine:page');
|
||||
rootService.viewport.onResize();
|
||||
|
||||
if (xywh) {
|
||||
@@ -60,12 +59,14 @@ function fitViewport(
|
||||
|
||||
export function DocPeekPreview({
|
||||
docId,
|
||||
blockId,
|
||||
blockIds,
|
||||
elementIds,
|
||||
mode,
|
||||
xywh,
|
||||
}: {
|
||||
docId: string;
|
||||
blockId?: string;
|
||||
blockIds?: string[];
|
||||
elementIds?: string[];
|
||||
mode?: DocMode;
|
||||
xywh?: `[${number},${number},${number},${number}]`;
|
||||
}) {
|
||||
@@ -97,7 +98,7 @@ export function DocPeekPreview({
|
||||
useEffect(() => {
|
||||
if (!mode || !resolvedMode) {
|
||||
setResolvedMode(
|
||||
docs.list.doc$(docId).value?.primaryMode$.value || 'page'
|
||||
docs.list.doc$(docId).value?.primaryMode$.value || DocMode.Page
|
||||
);
|
||||
}
|
||||
}, [docId, docs.list, resolvedMode, mode]);
|
||||
@@ -125,12 +126,17 @@ export function DocPeekPreview({
|
||||
return;
|
||||
}
|
||||
|
||||
const rootService =
|
||||
editorElement.host.std.spec.getService('affine:page');
|
||||
const rootService = editorElement.host.std.getService('affine:page');
|
||||
// doc change event inside peek view should be handled by peek view
|
||||
disposableGroup.add(
|
||||
rootService.slots.docLinkClicked.on(({ docId, blockId }) => {
|
||||
peekView.open({ docId, blockId }).catch(console.error);
|
||||
rootService.slots.docLinkClicked.on(options => {
|
||||
peekView
|
||||
.open({
|
||||
type: 'doc',
|
||||
docId: options.pageId,
|
||||
...options.params,
|
||||
})
|
||||
.catch(console.error);
|
||||
})
|
||||
);
|
||||
// TODO(@Peng): no tag peek view yet
|
||||
@@ -175,7 +181,8 @@ export function DocPeekPreview({
|
||||
ref={onRef}
|
||||
className={styles.editor}
|
||||
mode={resolvedMode}
|
||||
defaultSelectedBlockId={blockId}
|
||||
blockIds={blockIds}
|
||||
elementIds={elementIds}
|
||||
page={doc.blockSuiteDoc}
|
||||
/>
|
||||
</FrameworkScope>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { IconButton } from '@affine/component';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import type { DocMode } from '@blocksuite/blocks';
|
||||
import {
|
||||
CloseIcon,
|
||||
ExpandFullIcon,
|
||||
OpenInNewIcon,
|
||||
SplitViewIcon,
|
||||
} from '@blocksuite/icons/rc';
|
||||
import { type DocMode, useService } from '@toeverything/infra';
|
||||
import { useService } from '@toeverything/infra';
|
||||
import { clsx } from 'clsx';
|
||||
import {
|
||||
type HTMLAttributes,
|
||||
|
||||
@@ -27,13 +27,16 @@ function renderPeekView({ info }: ActivePeekView) {
|
||||
mode={info.mode}
|
||||
xywh={info.xywh}
|
||||
docId={info.docId}
|
||||
blockId={info.blockId}
|
||||
blockIds={info.blockIds}
|
||||
elementIds={info.elementIds}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (info.type === 'image') {
|
||||
return <ImagePreviewPeekView docId={info.docId} blockId={info.blockId} />;
|
||||
return (
|
||||
<ImagePreviewPeekView docId={info.docId} blockId={info.blockIds[0]} />
|
||||
);
|
||||
}
|
||||
|
||||
if (info.type === 'ai-chat-block') {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Doc, DocMode } from '@toeverything/infra';
|
||||
import type { DocMode } from '@blocksuite/blocks';
|
||||
import type { Doc } from '@toeverything/infra';
|
||||
import {
|
||||
DocsService,
|
||||
useLiveData,
|
||||
|
||||
@@ -4,7 +4,8 @@ import {
|
||||
type CommandCategory,
|
||||
PreconditionStrategy,
|
||||
} from '@affine/core/commands';
|
||||
import type { DocMode, GlobalContextService } from '@toeverything/infra';
|
||||
import type { DocMode } from '@blocksuite/blocks';
|
||||
import type { GlobalContextService } from '@toeverything/infra';
|
||||
import { Entity, LiveData } from '@toeverything/infra';
|
||||
import Fuse from 'fuse.js';
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { DocMode } from '@blocksuite/blocks';
|
||||
import { EdgelessIcon, PageIcon } from '@blocksuite/icons/rc';
|
||||
import { type DocMode, Entity, LiveData } from '@toeverything/infra';
|
||||
import { Entity, LiveData } from '@toeverything/infra';
|
||||
|
||||
import type { QuickSearchSession } from '../providers/quick-search-provider';
|
||||
import type { QuickSearchGroup } from '../types/group';
|
||||
|
||||
@@ -66,7 +66,7 @@ export class DocsQuickSearchSession
|
||||
{
|
||||
docId: resolvedDoc.docId,
|
||||
score: 100,
|
||||
blockId: resolvedDoc.blockId,
|
||||
blockId: resolvedDoc.blockIds?.[0],
|
||||
blockContent: '',
|
||||
},
|
||||
...docs,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { track } from '@affine/core/mixpanel';
|
||||
import { DocMode } from '@blocksuite/blocks';
|
||||
import type { DocsService } from '@toeverything/infra';
|
||||
import { Service } from '@toeverything/infra';
|
||||
|
||||
@@ -67,13 +68,13 @@ export class CMDKQuickSearchService extends Service {
|
||||
} else if (result.source === 'creation') {
|
||||
if (result.id === 'creation:create-page') {
|
||||
const newDoc = this.docsService.createDoc({
|
||||
primaryMode: 'page',
|
||||
primaryMode: DocMode.Page,
|
||||
title: result.payload.title,
|
||||
});
|
||||
this.workbenchService.workbench.openDoc(newDoc.id);
|
||||
} else if (result.id === 'creation:create-edgeless') {
|
||||
const newDoc = this.docsService.createDoc({
|
||||
primaryMode: 'edgeless',
|
||||
primaryMode: DocMode.Edgeless,
|
||||
title: result.payload.title,
|
||||
});
|
||||
this.workbenchService.workbench.openDoc(newDoc.id);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { UserFriendlyError } from '@affine/graphql';
|
||||
import type { DocMode } from '@blocksuite/blocks';
|
||||
import {
|
||||
type DocMode,
|
||||
effect,
|
||||
Entity,
|
||||
fromPromise,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ErrorNames, UserFriendlyError } from '@affine/graphql';
|
||||
import { type DocMode, Store } from '@toeverything/infra';
|
||||
import type { DocMode } from '@blocksuite/blocks';
|
||||
import { Store } from '@toeverything/infra';
|
||||
|
||||
import { type FetchService, isBackendError } from '../../cloud';
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Entity, LiveData } from '@toeverything/infra';
|
||||
import type { Location, To } from 'history';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import type { ParseOptions } from 'query-string';
|
||||
import queryString from 'query-string';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@@ -79,15 +80,14 @@ export class View extends Entity<{
|
||||
|
||||
icon$ = new LiveData(this.props.icon ?? 'allDocs');
|
||||
|
||||
queryString$<T extends Record<string, unknown>>({
|
||||
parseNumbers = true,
|
||||
}: { parseNumbers?: boolean } = {}) {
|
||||
queryString$<T extends Record<string, unknown>>(
|
||||
options: ParseOptions = {
|
||||
parseNumbers: true,
|
||||
parseBooleans: true,
|
||||
}
|
||||
) {
|
||||
return this.location$.map(
|
||||
location =>
|
||||
queryString.parse(location.search, {
|
||||
parseBooleans: true,
|
||||
parseNumbers: parseNumbers,
|
||||
}) as Partial<T>
|
||||
location => queryString.parse(location.search, options) as Partial<T>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -128,9 +128,15 @@ export class Workbench extends Entity {
|
||||
const docId = typeof id === 'string' ? id : id.docId;
|
||||
const blockId = typeof id === 'string' ? undefined : id.blockId;
|
||||
const mode = typeof id === 'string' ? undefined : id.mode;
|
||||
const hash = blockId ? `#${blockId}` : '';
|
||||
const query = mode ? `?mode=${mode}` : '';
|
||||
this.open(`/${docId}${query}${hash}`, options);
|
||||
let query = '';
|
||||
if (mode || blockId) {
|
||||
const search = new URLSearchParams();
|
||||
if (mode) search.set('mode', mode);
|
||||
if (blockId) search.set('blockIds', blockId);
|
||||
query = `?${search.toString()}`;
|
||||
}
|
||||
|
||||
this.open(`/${docId}${query}`, options);
|
||||
}
|
||||
|
||||
openCollections(options?: WorkbenchOpenOptions) {
|
||||
|
||||
Reference in New Issue
Block a user