refactor(editor): reorg block specs (#9421)

This commit is contained in:
Saul-Mirone
2024-12-30 05:59:25 +00:00
parent 87331b49b7
commit e3b6841944
16 changed files with 121 additions and 173 deletions
@@ -509,17 +509,6 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
}
});
// Forward docLinkClicked event from the synced doc
const refNodeProvider =
this.syncedDocEditorHost?.std.getOptional(RefNodeSlotsProvider);
if (refNodeProvider) {
this.disposables.add(
refNodeProvider.docLinkClicked.on(args => {
this.std.getOptional(RefNodeSlotsProvider)?.docLinkClicked.emit(args);
})
);
}
this._initEdgelessFitEffect();
}
@@ -7,14 +7,12 @@ import type { RefNodeSlots } from '../inline/index.js';
export const RefNodeSlotsProvider =
createIdentifier<RefNodeSlots>('AffineRefNodeSlots');
export function RefNodeSlotsExtension(
slots: RefNodeSlots = {
docLinkClicked: new Slot(),
}
): ExtensionType {
return {
setup: di => {
di.addImpl(RefNodeSlotsProvider, () => slots);
},
};
}
const slots: RefNodeSlots = {
docLinkClicked: new Slot(),
};
export const RefNodeSlotsExtension: ExtensionType = {
setup: di => {
di.addImpl(RefNodeSlotsProvider, () => slots);
},
};
@@ -14,4 +14,14 @@ export class SpecBuilder {
extend(extensions: ExtensionType[]) {
this._value = [...this._value, ...extensions];
}
omit(target: ExtensionType) {
this._value = this._value.filter(extension => extension !== target);
}
replace(target: ExtensionType, newExtension: ExtensionType) {
this._value = this._value.map(extension =>
extension === target ? newExtension : extension
);
}
}
@@ -46,6 +46,15 @@ export class SpecProvider {
return this.specMap.has(id);
}
cloneSpec(id: string, targetId: string) {
const existingSpec = this.specMap.get(id);
if (!existingSpec) {
console.error(`Spec not found for ${id}`);
return;
}
this.specMap.set(targetId, [...existingSpec]);
}
omitSpec(id: string, targetSpec: ExtensionType) {
const existingSpec = this.specMap.get(id);
if (!existingSpec) {
@@ -58,4 +67,17 @@ export class SpecProvider {
existingSpec.filter(spec => spec !== targetSpec)
);
}
replaceSpec(id: string, targetSpec: ExtensionType, newSpec: ExtensionType) {
const existingSpec = this.specMap.get(id);
if (!existingSpec) {
console.error(`Spec not found for ${id}`);
return;
}
this.specMap.set(
id,
existingSpec.map(spec => (spec === targetSpec ? newSpec : spec))
);
}
}
+42 -22
View File
@@ -2,52 +2,72 @@ import { AttachmentBlockSpec } from '@blocksuite/affine-block-attachment';
import { BookmarkBlockSpec } from '@blocksuite/affine-block-bookmark';
import { CodeBlockSpec } from '@blocksuite/affine-block-code';
import { DividerBlockSpec } from '@blocksuite/affine-block-divider';
import { EdgelessTextBlockSpec } from '@blocksuite/affine-block-edgeless-text';
import { EmbedExtensions } from '@blocksuite/affine-block-embed';
import { FrameBlockSpec } from '@blocksuite/affine-block-frame';
import { ImageBlockSpec } from '@blocksuite/affine-block-image';
import { LatexBlockSpec } from '@blocksuite/affine-block-latex';
import { ListBlockSpec } from '@blocksuite/affine-block-list';
import {
EdgelessNoteBlockSpec,
NoteBlockSpec,
} from '@blocksuite/affine-block-note';
import { ParagraphBlockSpec } from '@blocksuite/affine-block-paragraph';
import { RichTextExtensions } from '@blocksuite/affine-components/rich-text';
import { EditPropsStore } from '@blocksuite/affine-shared/services';
import {
EdgelessSurfaceBlockSpec,
PageSurfaceBlockSpec,
} from '@blocksuite/affine-block-surface';
import {
RefNodeSlotsExtension,
RichTextExtensions,
} from '@blocksuite/affine-components/rich-text';
import {
EditPropsStore,
FontLoaderService,
} from '@blocksuite/affine-shared/services';
import type { ExtensionType } from '@blocksuite/block-std';
import { AdapterFactoryExtensions } from '../_common/adapters/extension.js';
import { DataViewBlockSpec } from '../data-view-block/data-view-spec.js';
import { DatabaseBlockSpec } from '../database-block/database-spec.js';
import {
EdgelessSurfaceRefBlockSpec,
PageSurfaceRefBlockSpec,
} from '../surface-ref-block/surface-ref-spec.js';
export const CommonFirstPartyBlockSpecs: ExtensionType[] = [
RichTextExtensions,
export const CommonBlockSpecs: ExtensionType[] = [
RefNodeSlotsExtension,
EditPropsStore,
RichTextExtensions,
LatexBlockSpec,
ListBlockSpec,
NoteBlockSpec,
DatabaseBlockSpec,
DataViewBlockSpec,
DividerBlockSpec,
BookmarkBlockSpec,
EmbedExtensions,
AttachmentBlockSpec,
AdapterFactoryExtensions,
CodeBlockSpec,
ImageBlockSpec,
ParagraphBlockSpec,
BookmarkBlockSpec,
AttachmentBlockSpec,
EmbedExtensions,
AdapterFactoryExtensions,
].flat();
export const PageFirstPartyBlockSpecs: ExtensionType[] = [
...CommonBlockSpecs,
NoteBlockSpec,
PageSurfaceBlockSpec,
PageSurfaceRefBlockSpec,
FontLoaderService,
].flat();
export const EdgelessFirstPartyBlockSpecs: ExtensionType[] = [
RichTextExtensions,
EditPropsStore,
ListBlockSpec,
...CommonBlockSpecs,
EdgelessNoteBlockSpec,
DatabaseBlockSpec,
DataViewBlockSpec,
DividerBlockSpec,
CodeBlockSpec,
ImageBlockSpec,
ParagraphBlockSpec,
BookmarkBlockSpec,
AttachmentBlockSpec,
EmbedExtensions,
AdapterFactoryExtensions,
EdgelessSurfaceBlockSpec,
EdgelessSurfaceRefBlockSpec,
FrameBlockSpec,
EdgelessTextBlockSpec,
FontLoaderService,
].flat();
@@ -1,11 +1,4 @@
import { EdgelessTextBlockSpec } from '@blocksuite/affine-block-edgeless-text';
import { FrameBlockSpec } from '@blocksuite/affine-block-frame';
import { LatexBlockSpec } from '@blocksuite/affine-block-latex';
import {
ConnectionOverlay,
EdgelessSurfaceBlockSpec,
} from '@blocksuite/affine-block-surface';
import { FontLoaderService } from '@blocksuite/affine-shared/services';
import { ConnectionOverlay } from '@blocksuite/affine-block-surface';
import type { ExtensionType } from '@blocksuite/block-std';
import { EdgelessRootBlockSpec } from '../../root-block/edgeless/edgeless-root-spec.js';
@@ -30,7 +23,6 @@ import { TemplateTool } from '../../root-block/edgeless/gfx-tool/template-tool.j
import { TextTool } from '../../root-block/edgeless/gfx-tool/text-tool.js';
import { EditPropsMiddlewareBuilder } from '../../root-block/edgeless/middlewares/base.js';
import { EdgelessSnapManager } from '../../root-block/edgeless/utils/snap-manager.js';
import { EdgelessSurfaceRefBlockSpec } from '../../surface-ref-block/surface-ref-spec.js';
import { EdgelessFirstPartyBlockSpecs } from '../common.js';
export const EdgelessToolExtension: ExtensionType[] = [
@@ -62,12 +54,6 @@ export const EdgelessBuiltInManager: ExtensionType[] = [
export const EdgelessEditorBlockSpecs: ExtensionType[] = [
EdgelessRootBlockSpec,
...EdgelessFirstPartyBlockSpecs,
EdgelessSurfaceBlockSpec,
EdgelessSurfaceRefBlockSpec,
FrameBlockSpec,
EdgelessTextBlockSpec,
LatexBlockSpec,
FontLoaderService,
EdgelessToolExtension,
EdgelessBuiltInManager,
].flat();
@@ -1,17 +1,9 @@
import { LatexBlockSpec } from '@blocksuite/affine-block-latex';
import { PageSurfaceBlockSpec } from '@blocksuite/affine-block-surface';
import { FontLoaderService } from '@blocksuite/affine-shared/services';
import type { ExtensionType } from '@blocksuite/block-std';
import { PageRootBlockSpec } from '../../root-block/page/page-root-spec.js';
import { PageSurfaceRefBlockSpec } from '../../surface-ref-block/surface-ref-spec.js';
import { CommonFirstPartyBlockSpecs } from '../common.js';
import { PageFirstPartyBlockSpecs } from '../common.js';
export const PageEditorBlockSpecs: ExtensionType[] = [
PageRootBlockSpec,
...CommonFirstPartyBlockSpecs,
PageSurfaceBlockSpec,
PageSurfaceRefBlockSpec,
LatexBlockSpec,
FontLoaderService,
...PageFirstPartyBlockSpecs,
].flat();
@@ -1,64 +1,18 @@
import { EdgelessTextBlockSpec } from '@blocksuite/affine-block-edgeless-text';
import { FrameBlockSpec } from '@blocksuite/affine-block-frame';
import { LatexBlockSpec } from '@blocksuite/affine-block-latex';
import {
EdgelessSurfaceBlockSpec,
PageSurfaceBlockSpec,
} from '@blocksuite/affine-block-surface';
import { RefNodeSlotsExtension } from '@blocksuite/affine-components/rich-text';
import {
DocDisplayMetaService,
DocModeService,
EmbedOptionService,
FontLoaderService,
ThemeService,
} from '@blocksuite/affine-shared/services';
import {
BlockViewExtension,
type ExtensionType,
FlavourExtension,
} from '@blocksuite/block-std';
import { literal } from 'lit/static-html.js';
import type { ExtensionType } from '@blocksuite/block-std';
import { PreviewEdgelessRootBlockSpec } from '../../root-block/edgeless/edgeless-root-spec.js';
import { PageRootService } from '../../root-block/page/page-root-service.js';
import { PreviewPageRootBlockSpec } from '../../root-block/page/page-root-spec.js';
import {
EdgelessSurfaceRefBlockSpec,
PageSurfaceRefBlockSpec,
} from '../../surface-ref-block/surface-ref-spec.js';
import {
CommonFirstPartyBlockSpecs,
EdgelessFirstPartyBlockSpecs,
PageFirstPartyBlockSpecs,
} from '../common.js';
const PreviewPageSpec: ExtensionType[] = [
FlavourExtension('affine:page'),
PageRootService,
DocModeService,
ThemeService,
EmbedOptionService,
BlockViewExtension('affine:page', literal`affine-preview-root`),
DocDisplayMetaService,
];
export const PreviewEdgelessEditorBlockSpecs: ExtensionType[] = [
PreviewEdgelessRootBlockSpec,
...EdgelessFirstPartyBlockSpecs,
EdgelessSurfaceBlockSpec,
EdgelessSurfaceRefBlockSpec,
FrameBlockSpec,
EdgelessTextBlockSpec,
LatexBlockSpec,
FontLoaderService,
RefNodeSlotsExtension(),
EdgelessFirstPartyBlockSpecs,
].flat();
export const PreviewEditorBlockSpecs: ExtensionType[] = [
PreviewPageSpec,
...CommonFirstPartyBlockSpecs,
PageSurfaceBlockSpec,
PageSurfaceRefBlockSpec,
LatexBlockSpec,
FontLoaderService,
RefNodeSlotsExtension(),
export const PreviewPageEditorBlockSpecs: ExtensionType[] = [
PreviewPageRootBlockSpec,
PageFirstPartyBlockSpecs,
].flat();
@@ -1,16 +1,21 @@
import { SpecProvider } from '@blocksuite/affine-shared/utils';
import { CommonBlockSpecs } from './common.js';
import { EdgelessEditorBlockSpecs } from './preset/edgeless-specs.js';
import { PageEditorBlockSpecs } from './preset/page-specs.js';
import {
PreviewEdgelessEditorBlockSpecs,
PreviewEditorBlockSpecs,
PreviewPageEditorBlockSpecs,
} from './preset/preview-specs.js';
export function registerSpecs() {
SpecProvider.getInstance().addSpec('common', CommonBlockSpecs);
SpecProvider.getInstance().addSpec('page', PageEditorBlockSpecs);
SpecProvider.getInstance().addSpec('edgeless', EdgelessEditorBlockSpecs);
SpecProvider.getInstance().addSpec('page:preview', PreviewEditorBlockSpecs);
SpecProvider.getInstance().addSpec(
'page:preview',
PreviewPageEditorBlockSpecs
);
SpecProvider.getInstance().addSpec(
'edgeless:preview',
PreviewEdgelessEditorBlockSpecs
@@ -64,19 +64,28 @@ export const pageRootWidgetViewMap = {
[AFFINE_SCROLL_ANCHORING_WIDGET]: literal`${unsafeStatic(AFFINE_SCROLL_ANCHORING_WIDGET)}`,
};
export const PageRootBlockSpec: ExtensionType[] = [
const PageCommonExtension: ExtensionType[] = [
FlavourExtension('affine:page'),
PageRootService,
DocModeService,
ThemeService,
EmbedOptionService,
CommandExtension(commands),
DocDisplayMetaService,
PageViewportServiceExtension,
];
export const PageRootBlockSpec: ExtensionType[] = [
...PageCommonExtension,
BlockViewExtension('affine:page', literal`affine-page-root`),
CommandExtension(commands),
WidgetViewMapExtension('affine:page', pageRootWidgetViewMap),
ExportManagerExtension,
DNDAPIExtension,
PageViewportServiceExtension,
DocDisplayMetaService,
RootBlockAdapterExtensions,
FileDropExtension,
].flat();
export const PreviewPageRootBlockSpec: ExtensionType[] = [
...PageCommonExtension,
BlockViewExtension('affine:page', literal`affine-preview-root`),
];
@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import type { EditorHost, ExtensionType } from '@blocksuite/block-std';
import {
CommunityCanvasTextFonts,
@@ -10,7 +9,6 @@ import {
NotificationExtension,
OverrideThemeExtension,
ParseDocUrlExtension,
RefNodeSlotsExtension,
RefNodeSlotsProvider,
SpecProvider,
} from '@blocksuite/blocks';
@@ -47,15 +45,8 @@ export async function mountDefaultDocEditor(collection: DocCollection) {
const editor = new AffineEditorContainer();
const specs = getExampleSpecs();
const refNodeSlotsExtension = RefNodeSlotsExtension();
editor.pageSpecs = patchPageRootSpec([
refNodeSlotsExtension,
...specs.pageModeSpecs,
]);
editor.edgelessSpecs = patchPageRootSpec([
refNodeSlotsExtension,
...specs.edgelessModeSpecs,
]);
editor.pageSpecs = patchPageRootSpec([...specs.pageModeSpecs]);
editor.edgelessSpecs = patchPageRootSpec([...specs.edgelessModeSpecs]);
SpecProvider.getInstance().extendSpec('edgeless:preview', [
OverrideThemeExtension(themeExtension),
@@ -13,7 +13,6 @@ import {
FontConfigExtension,
ParseDocUrlProvider,
QuickSearchProvider,
RefNodeSlotsExtension,
RefNodeSlotsProvider,
} from '@blocksuite/blocks';
import { effects as blocksEffects } from '@blocksuite/blocks/effects';
@@ -60,12 +59,10 @@ async function main() {
},
defaultExtensions: (): ExtensionType[] => [
FontConfigExtension(CommunityCanvasTextFonts),
RefNodeSlotsExtension(),
],
extensions: {
FontConfigExtension: FontConfigExtension(CommunityCanvasTextFonts),
WidgetViewMapExtension,
RefNodeSlotsExtension: RefNodeSlotsExtension(),
},
mockServices: {
mockDocModeService,
@@ -13,7 +13,6 @@ import {
OverrideThemeExtension,
type PageRootService,
ParseDocUrlExtension,
RefNodeSlotsExtension,
RefNodeSlotsProvider,
SpecProvider,
toolbarDefaultConfig,
@@ -73,9 +72,7 @@ export async function mountDefaultDocEditor(collection: DocCollection) {
}
}
const refNodeSlotsExtension = RefNodeSlotsExtension();
const extensions: ExtensionType[] = [
refNodeSlotsExtension,
PatchPageServiceWatcher,
FontConfigExtension(CommunityCanvasTextFonts),
ParseDocUrlExtension(mockParseDocUrlService(collection)),
@@ -1892,7 +1892,7 @@ export async function createNote(
content?: string
) {
const start = await toViewCoord(page, coord1);
return addNote(page, content || 'note', start[0], start[1]);
return addNote(page, content ?? 'note', start[0], start[1]);
}
export async function hoverOnNote(page: Page, id: string, offset = [0, 0]) {
@@ -14,13 +14,7 @@ import {
DataViewBlockSpec,
DividerBlockSpec,
EditPropsStore,
EmbedFigmaBlockSpec,
EmbedGithubBlockSpec,
EmbedHtmlBlockSpec,
EmbedLinkedDocBlockSpec,
EmbedLoomBlockSpec,
EmbedSyncedDocBlockSpec,
EmbedYoutubeBlockSpec,
EmbedExtensions,
ImageBlockSpec,
LatexBlockSpec,
ListBlockSpec,
@@ -30,7 +24,7 @@ import {
} from '@blocksuite/affine/blocks';
const CommonBlockSpecs: ExtensionType[] = [
RefNodeSlotsExtension(),
RefNodeSlotsExtension,
EditPropsStore,
RichTextExtensions,
LatexBlockSpec,
@@ -38,16 +32,9 @@ const CommonBlockSpecs: ExtensionType[] = [
DatabaseBlockSpec,
DataViewBlockSpec,
DividerBlockSpec,
EmbedExtensions,
BookmarkBlockSpec,
EmbedFigmaBlockSpec,
EmbedGithubBlockSpec,
EmbedYoutubeBlockSpec,
EmbedLoomBlockSpec,
EmbedHtmlBlockSpec,
EmbedSyncedDocBlockSpec,
EmbedLinkedDocBlockSpec,
AttachmentBlockSpec,
// special
AdapterFactoryExtensions,
].flat();
@@ -29,7 +29,6 @@ import { isNewTabTrigger } from '@affine/core/utils';
import { DebugLogger } from '@affine/debug';
import { track } from '@affine/track';
import {
type BlockService,
BlockServiceWatcher,
BlockViewIdentifier,
ConfigIdentifier,
@@ -44,7 +43,6 @@ import type {
PeekViewService as BSPeekViewService,
QuickSearchResult,
RootBlockConfig,
RootService,
} from '@blocksuite/affine/blocks';
import {
AffineSlashMenuWidget,
@@ -81,21 +79,15 @@ export type ReferenceReactRenderer = (
const logger = new DebugLogger('affine::spec-patchers');
function patchSpecService<Service extends BlockService = BlockService>(
function patchSpecService(
flavour: string,
onMounted: (service: Service) => (() => void) | void,
onWidgetConnected?: (component: WidgetComponent) => void
) {
class TempServiceWatcher extends BlockServiceWatcher {
static override readonly flavour = flavour;
override mounted() {
super.mounted();
const disposable = onMounted(this.blockService as any);
const disposableGroup = this.blockService.disposables;
if (disposable) {
disposableGroup.add(disposable);
}
if (onWidgetConnected) {
disposableGroup.add(
this.blockService.specSlots.widgetConnected.on(({ component }) => {
@@ -424,9 +416,8 @@ export function patchQuickSearchService(framework: FrameworkProvider) {
},
});
const SlashMenuQuickSearchExtension = patchSpecService<RootService>(
const SlashMenuQuickSearchExtension = patchSpecService(
'affine:page',
() => {},
(component: WidgetComponent) => {
if (component instanceof AffineSlashMenuWidget) {
component.config.items.forEach(item => {