feat(editor): add WidgetViewExtension (#10180)

Closes: [BS-2282](https://linear.app/affine-design/issue/BS-2282/replace-widgetviewmapextension-with-widgetextension)
This commit is contained in:
Saul-Mirone
2025-02-14 11:00:01 +00:00
parent 9dc81ecb99
commit d111f8ac88
19 changed files with 324 additions and 313 deletions

View File

@@ -1,7 +1,7 @@
import { import {
BlockViewExtension, BlockViewExtension,
FlavourExtension, FlavourExtension,
WidgetViewMapExtension, WidgetViewExtension,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import type { ExtensionType } from '@blocksuite/store'; import type { ExtensionType } from '@blocksuite/store';
import { literal, unsafeStatic } from 'lit/static-html.js'; import { literal, unsafeStatic } from 'lit/static-html.js';
@@ -14,13 +14,17 @@ import {
import { CodeBlockService } from './code-block-service.js'; import { CodeBlockService } from './code-block-service.js';
import { AFFINE_CODE_TOOLBAR_WIDGET } from './code-toolbar/index.js'; import { AFFINE_CODE_TOOLBAR_WIDGET } from './code-toolbar/index.js';
export const codeToolbarWidget = WidgetViewExtension(
'affine:code',
AFFINE_CODE_TOOLBAR_WIDGET,
literal`${unsafeStatic(AFFINE_CODE_TOOLBAR_WIDGET)}`
);
export const CodeBlockSpec: ExtensionType[] = [ export const CodeBlockSpec: ExtensionType[] = [
FlavourExtension('affine:code'), FlavourExtension('affine:code'),
CodeBlockService, CodeBlockService,
BlockViewExtension('affine:code', literal`affine-code`), BlockViewExtension('affine:code', literal`affine-code`),
WidgetViewMapExtension('affine:code', { codeToolbarWidget,
codeToolbar: literal`${unsafeStatic(AFFINE_CODE_TOOLBAR_WIDGET)}`,
}),
CodeBlockInlineManagerExtension, CodeBlockInlineManagerExtension,
CodeBlockUnitSpecExtension, CodeBlockUnitSpecExtension,
CodeBlockAdapterExtensions, CodeBlockAdapterExtensions,

View File

@@ -1,7 +1,7 @@
import { import {
BlockViewExtension, BlockViewExtension,
FlavourExtension, FlavourExtension,
WidgetViewMapExtension, WidgetViewExtension,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import type { ExtensionType } from '@blocksuite/store'; import type { ExtensionType } from '@blocksuite/store';
import { literal } from 'lit/static-html.js'; import { literal } from 'lit/static-html.js';
@@ -10,6 +10,12 @@ import { ImageBlockAdapterExtensions } from './adapters/extension.js';
import { ImageProxyService } from './image-proxy-service.js'; import { ImageProxyService } from './image-proxy-service.js';
import { ImageBlockService, ImageDropOption } from './image-service.js'; import { ImageBlockService, ImageDropOption } from './image-service.js';
export const imageToolbarWidget = WidgetViewExtension(
'affine:image',
'imageToolbar',
literal`affine-image-toolbar-widget`
);
export const ImageBlockSpec: ExtensionType[] = [ export const ImageBlockSpec: ExtensionType[] = [
FlavourExtension('affine:image'), FlavourExtension('affine:image'),
ImageBlockService, ImageBlockService,
@@ -22,9 +28,7 @@ export const ImageBlockSpec: ExtensionType[] = [
return literal`affine-image`; return literal`affine-image`;
}), }),
WidgetViewMapExtension('affine:image', { imageToolbarWidget,
imageToolbar: literal`affine-image-toolbar-widget`,
}),
ImageDropOption, ImageDropOption,
ImageBlockAdapterExtensions, ImageBlockAdapterExtensions,
].flat(); ].flat();

View File

@@ -1,9 +1,6 @@
export * from './commands.js'; export * from './commands.js';
export * from './surface-ref-block.js'; export * from './surface-ref-block.js';
export * from './surface-ref-block-edgeless.js'; export * from './surface-ref-block-edgeless.js';
export { export * from './surface-ref-spec.js';
EdgelessSurfaceRefBlockSpec,
PageSurfaceRefBlockSpec,
} from './surface-ref-spec.js';
export * from './types.js'; export * from './types.js';
export * from './utils.js'; export * from './utils.js';

View File

@@ -1,17 +1,21 @@
import { import {
BlockViewExtension, BlockViewExtension,
FlavourExtension, FlavourExtension,
WidgetViewMapExtension, WidgetViewExtension,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import type { ExtensionType } from '@blocksuite/store'; import type { ExtensionType } from '@blocksuite/store';
import { literal } from 'lit/static-html.js'; import { literal } from 'lit/static-html.js';
export const surfaceRefToolbarWidget = WidgetViewExtension(
'affine:surface-ref',
'surfaceToolbar',
literal`affine-surface-ref-toolbar`
);
export const PageSurfaceRefBlockSpec: ExtensionType[] = [ export const PageSurfaceRefBlockSpec: ExtensionType[] = [
FlavourExtension('affine:surface-ref'), FlavourExtension('affine:surface-ref'),
BlockViewExtension('affine:surface-ref', literal`affine-surface-ref`), BlockViewExtension('affine:surface-ref', literal`affine-surface-ref`),
WidgetViewMapExtension('affine:surface-ref', { surfaceRefToolbarWidget,
surfaceToolbar: literal`affine-surface-ref-toolbar`,
}),
]; ];
export const EdgelessSurfaceRefBlockSpec: ExtensionType[] = [ export const EdgelessSurfaceRefBlockSpec: ExtensionType[] = [

View File

@@ -9,15 +9,9 @@ import {
type BlockStdScope, type BlockStdScope,
ConfigIdentifier, ConfigIdentifier,
LifeCycleWatcher, LifeCycleWatcher,
WidgetViewMapIdentifier,
type WidgetViewMapType,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import type { Container } from '@blocksuite/global/di'; import type { Container } from '@blocksuite/global/di';
import { AFFINE_EMBED_CARD_TOOLBAR_WIDGET } from '../../root-block/widgets/embed-card-toolbar/embed-card-toolbar.js';
import { AFFINE_FORMAT_BAR_WIDGET } from '../../root-block/widgets/format-bar/format-bar.js';
import { AFFINE_SLASH_MENU_WIDGET } from '../../root-block/widgets/slash-menu/index.js';
export class MobileSpecsPatches extends LifeCycleWatcher { export class MobileSpecsPatches extends LifeCycleWatcher {
static override key = 'mobile-patches'; static override key = 'mobile-patches';
@@ -54,48 +48,6 @@ export class MobileSpecsPatches extends LifeCycleWatcher {
} satisfies CodeBlockConfig; } satisfies CodeBlockConfig;
}); });
} }
// Disable root level widgets for mobile.
{
const rootWidgetViewMapIdentifier =
WidgetViewMapIdentifier('affine:page');
const prev = di.getFactory(rootWidgetViewMapIdentifier);
di.override(rootWidgetViewMapIdentifier, provider => {
const ignoreWidgets = [
AFFINE_FORMAT_BAR_WIDGET,
AFFINE_EMBED_CARD_TOOLBAR_WIDGET,
AFFINE_SLASH_MENU_WIDGET,
];
const newMap = { ...prev?.(provider) };
ignoreWidgets.forEach(widget => {
if (widget in newMap) delete newMap[widget];
});
return newMap;
});
}
// Disable block level toolbar widgets for mobile.
{
di.override(
WidgetViewMapIdentifier('affine:code'),
(): WidgetViewMapType => ({})
);
di.override(
WidgetViewMapIdentifier('affine:image'),
(): WidgetViewMapType => ({})
);
di.override(
WidgetViewMapIdentifier('affine:surface-ref'),
(): WidgetViewMapType => ({})
);
}
} }
override mounted() { override mounted() {

View File

@@ -0,0 +1,50 @@
import { FileDropExtension } from '@blocksuite/affine-components/drop-indicator';
import {
DNDAPIExtension,
DocModeService,
EmbedOptionService,
PageViewportServiceExtension,
ThemeService,
} from '@blocksuite/affine-shared/services';
import { FlavourExtension } from '@blocksuite/block-std';
import type { ExtensionType } from '@blocksuite/store';
import { ExportManagerExtension } from '../../_common/export-manager/export-manager';
import { RootBlockAdapterExtensions } from '../adapters/extension';
import {
docRemoteSelectionWidget,
dragHandleWidget,
embedCardToolbarWidget,
formatBarWidget,
innerModalWidget,
linkedDocWidget,
modalWidget,
scrollAnchoringWidget,
slashMenuWidget,
viewportOverlayWidget,
} from './widgets';
export const CommonSpecs: ExtensionType[] = [
FlavourExtension('affine:page'),
DocModeService,
ThemeService,
EmbedOptionService,
ExportManagerExtension,
PageViewportServiceExtension,
DNDAPIExtension,
FileDropExtension,
...RootBlockAdapterExtensions,
modalWidget,
innerModalWidget,
slashMenuWidget,
linkedDocWidget,
dragHandleWidget,
embedCardToolbarWidget,
formatBarWidget,
docRemoteSelectionWidget,
viewportOverlayWidget,
scrollAnchoringWidget,
];
export * from './widgets';

View File

@@ -0,0 +1,64 @@
import { AFFINE_DRAG_HANDLE_WIDGET } from '@blocksuite/affine-widget-drag-handle';
import { AFFINE_DOC_REMOTE_SELECTION_WIDGET } from '@blocksuite/affine-widget-remote-selection';
import { AFFINE_SCROLL_ANCHORING_WIDGET } from '@blocksuite/affine-widget-scroll-anchoring';
import { WidgetViewExtension } from '@blocksuite/block-std';
import { literal, unsafeStatic } from 'lit/static-html.js';
import { AFFINE_EMBED_CARD_TOOLBAR_WIDGET } from '../widgets/embed-card-toolbar/embed-card-toolbar.js';
import { AFFINE_FORMAT_BAR_WIDGET } from '../widgets/format-bar/format-bar.js';
import { AFFINE_INNER_MODAL_WIDGET } from '../widgets/inner-modal/inner-modal.js';
import { AFFINE_LINKED_DOC_WIDGET } from '../widgets/linked-doc/config.js';
import { AFFINE_MODAL_WIDGET } from '../widgets/modal/modal.js';
import { AFFINE_SLASH_MENU_WIDGET } from '../widgets/slash-menu/index.js';
import { AFFINE_VIEWPORT_OVERLAY_WIDGET } from '../widgets/viewport-overlay/viewport-overlay.js';
export const modalWidget = WidgetViewExtension(
'affine:page',
AFFINE_MODAL_WIDGET,
literal`${unsafeStatic(AFFINE_MODAL_WIDGET)}`
);
export const innerModalWidget = WidgetViewExtension(
'affine:page',
AFFINE_INNER_MODAL_WIDGET,
literal`${unsafeStatic(AFFINE_INNER_MODAL_WIDGET)}`
);
export const slashMenuWidget = WidgetViewExtension(
'affine:page',
AFFINE_SLASH_MENU_WIDGET,
literal`${unsafeStatic(AFFINE_SLASH_MENU_WIDGET)}`
);
export const linkedDocWidget = WidgetViewExtension(
'affine:page',
AFFINE_LINKED_DOC_WIDGET,
literal`${unsafeStatic(AFFINE_LINKED_DOC_WIDGET)}`
);
export const dragHandleWidget = WidgetViewExtension(
'affine:page',
AFFINE_DRAG_HANDLE_WIDGET,
literal`${unsafeStatic(AFFINE_DRAG_HANDLE_WIDGET)}`
);
export const embedCardToolbarWidget = WidgetViewExtension(
'affine:page',
AFFINE_EMBED_CARD_TOOLBAR_WIDGET,
literal`${unsafeStatic(AFFINE_EMBED_CARD_TOOLBAR_WIDGET)}`
);
export const formatBarWidget = WidgetViewExtension(
'affine:page',
AFFINE_FORMAT_BAR_WIDGET,
literal`${unsafeStatic(AFFINE_FORMAT_BAR_WIDGET)}`
);
export const docRemoteSelectionWidget = WidgetViewExtension(
'affine:page',
AFFINE_DOC_REMOTE_SELECTION_WIDGET,
literal`${unsafeStatic(AFFINE_DOC_REMOTE_SELECTION_WIDGET)}`
);
export const viewportOverlayWidget = WidgetViewExtension(
'affine:page',
AFFINE_VIEWPORT_OVERLAY_WIDGET,
literal`${unsafeStatic(AFFINE_VIEWPORT_OVERLAY_WIDGET)}`
);
export const scrollAnchoringWidget = WidgetViewExtension(
'affine:page',
AFFINE_SCROLL_ANCHORING_WIDGET,
literal`${unsafeStatic(AFFINE_SCROLL_ANCHORING_WIDGET)}`
);

View File

@@ -1,40 +1,18 @@
import { FileDropExtension } from '@blocksuite/affine-components/drop-indicator';
import {
DNDAPIExtension,
DocModeService,
EmbedOptionService,
PageViewportServiceExtension,
ThemeService,
} from '@blocksuite/affine-shared/services';
import { AFFINE_DRAG_HANDLE_WIDGET } from '@blocksuite/affine-widget-drag-handle';
import { AFFINE_EDGELESS_AUTO_CONNECT_WIDGET } from '@blocksuite/affine-widget-edgeless-auto-connect'; import { AFFINE_EDGELESS_AUTO_CONNECT_WIDGET } from '@blocksuite/affine-widget-edgeless-auto-connect';
import { AFFINE_FRAME_TITLE_WIDGET } from '@blocksuite/affine-widget-frame-title'; import { AFFINE_FRAME_TITLE_WIDGET } from '@blocksuite/affine-widget-frame-title';
import { import { AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET } from '@blocksuite/affine-widget-remote-selection';
AFFINE_DOC_REMOTE_SELECTION_WIDGET,
AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET,
} from '@blocksuite/affine-widget-remote-selection';
import { AFFINE_SCROLL_ANCHORING_WIDGET } from '@blocksuite/affine-widget-scroll-anchoring';
import { import {
BlockServiceWatcher, BlockServiceWatcher,
BlockViewExtension, BlockViewExtension,
FlavourExtension, WidgetViewExtension,
WidgetViewMapExtension,
} from '@blocksuite/block-std'; } from '@blocksuite/block-std';
import { ToolController } from '@blocksuite/block-std/gfx'; import { ToolController } from '@blocksuite/block-std/gfx';
import type { ExtensionType } from '@blocksuite/store'; import type { ExtensionType } from '@blocksuite/store';
import { literal, unsafeStatic } from 'lit/static-html.js'; import { literal, unsafeStatic } from 'lit/static-html.js';
import { ExportManagerExtension } from '../../_common/export-manager/export-manager.js'; import { CommonSpecs } from '../common-specs/index.js';
import { RootBlockAdapterExtensions } from '../adapters/extension.js';
import { AFFINE_EDGELESS_ZOOM_TOOLBAR_WIDGET } from '../widgets/edgeless-zoom-toolbar/index.js'; import { AFFINE_EDGELESS_ZOOM_TOOLBAR_WIDGET } from '../widgets/edgeless-zoom-toolbar/index.js';
import { EDGELESS_ELEMENT_TOOLBAR_WIDGET } from '../widgets/element-toolbar/index.js'; import { EDGELESS_ELEMENT_TOOLBAR_WIDGET } from '../widgets/element-toolbar/index.js';
import { AFFINE_EMBED_CARD_TOOLBAR_WIDGET } from '../widgets/embed-card-toolbar/embed-card-toolbar.js';
import { AFFINE_FORMAT_BAR_WIDGET } from '../widgets/format-bar/format-bar.js';
import { AFFINE_INNER_MODAL_WIDGET } from '../widgets/inner-modal/inner-modal.js';
import { AFFINE_LINKED_DOC_WIDGET } from '../widgets/linked-doc/config.js';
import { AFFINE_MODAL_WIDGET } from '../widgets/modal/modal.js';
import { AFFINE_SLASH_MENU_WIDGET } from '../widgets/slash-menu/index.js';
import { AFFINE_VIEWPORT_OVERLAY_WIDGET } from '../widgets/viewport-overlay/viewport-overlay.js';
import { NOTE_SLICER_WIDGET } from './components/note-slicer/index.js'; import { NOTE_SLICER_WIDGET } from './components/note-slicer/index.js';
import { EDGELESS_NAVIGATOR_BLACK_BACKGROUND_WIDGET } from './components/presentation/edgeless-navigator-black-background.js'; import { EDGELESS_NAVIGATOR_BLACK_BACKGROUND_WIDGET } from './components/presentation/edgeless-navigator-black-background.js';
import { EDGELESS_DRAGGING_AREA_WIDGET } from './components/rects/edgeless-dragging-area-rect.js'; import { EDGELESS_DRAGGING_AREA_WIDGET } from './components/rects/edgeless-dragging-area-rect.js';
@@ -42,68 +20,56 @@ import { EDGELESS_SELECTED_RECT_WIDGET } from './components/rects/edgeless-selec
import { EDGELESS_TOOLBAR_WIDGET } from './components/toolbar/edgeless-toolbar.js'; import { EDGELESS_TOOLBAR_WIDGET } from './components/toolbar/edgeless-toolbar.js';
import { EdgelessRootService } from './edgeless-root-service.js'; import { EdgelessRootService } from './edgeless-root-service.js';
export const edgelessRootWidgetViewMap = { export const edgelessRemoteSelectionWidget = WidgetViewExtension(
[AFFINE_MODAL_WIDGET]: literal`${unsafeStatic(AFFINE_MODAL_WIDGET)}`, 'affine:page',
[AFFINE_INNER_MODAL_WIDGET]: literal`${unsafeStatic(AFFINE_INNER_MODAL_WIDGET)}`, AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET,
[AFFINE_SLASH_MENU_WIDGET]: literal`${unsafeStatic( literal`${unsafeStatic(AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET)}`
AFFINE_SLASH_MENU_WIDGET );
)}`, export const edgelessZoomToolbarWidget = WidgetViewExtension(
[AFFINE_LINKED_DOC_WIDGET]: literal`${unsafeStatic( 'affine:page',
AFFINE_LINKED_DOC_WIDGET AFFINE_EDGELESS_ZOOM_TOOLBAR_WIDGET,
)}`, literal`${unsafeStatic(AFFINE_EDGELESS_ZOOM_TOOLBAR_WIDGET)}`
[AFFINE_DRAG_HANDLE_WIDGET]: literal`${unsafeStatic( );
AFFINE_DRAG_HANDLE_WIDGET export const frameTitleWidget = WidgetViewExtension(
)}`, 'affine:page',
[AFFINE_EMBED_CARD_TOOLBAR_WIDGET]: literal`${unsafeStatic( AFFINE_FRAME_TITLE_WIDGET,
AFFINE_EMBED_CARD_TOOLBAR_WIDGET literal`${unsafeStatic(AFFINE_FRAME_TITLE_WIDGET)}`
)}`, );
[AFFINE_FORMAT_BAR_WIDGET]: literal`${unsafeStatic( export const elementToolbarWidget = WidgetViewExtension(
AFFINE_FORMAT_BAR_WIDGET 'affine:page',
)}`, EDGELESS_ELEMENT_TOOLBAR_WIDGET,
[AFFINE_DOC_REMOTE_SELECTION_WIDGET]: literal`${unsafeStatic( literal`${unsafeStatic(EDGELESS_ELEMENT_TOOLBAR_WIDGET)}`
AFFINE_DOC_REMOTE_SELECTION_WIDGET );
)}`, export const autoConnectWidget = WidgetViewExtension(
[AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET]: literal`${unsafeStatic( 'affine:page',
AFFINE_EDGELESS_REMOTE_SELECTION_WIDGET AFFINE_EDGELESS_AUTO_CONNECT_WIDGET,
)}`, literal`${unsafeStatic(AFFINE_EDGELESS_AUTO_CONNECT_WIDGET)}`
[AFFINE_EDGELESS_ZOOM_TOOLBAR_WIDGET]: literal`${unsafeStatic( );
AFFINE_EDGELESS_ZOOM_TOOLBAR_WIDGET export const edgelessDraggingAreaWidget = WidgetViewExtension(
)}`, 'affine:page',
[AFFINE_FRAME_TITLE_WIDGET]: literal`${unsafeStatic(AFFINE_FRAME_TITLE_WIDGET)}`, EDGELESS_DRAGGING_AREA_WIDGET,
[EDGELESS_ELEMENT_TOOLBAR_WIDGET]: literal`${unsafeStatic(EDGELESS_ELEMENT_TOOLBAR_WIDGET)}`, literal`${unsafeStatic(EDGELESS_DRAGGING_AREA_WIDGET)}`
[AFFINE_VIEWPORT_OVERLAY_WIDGET]: literal`${unsafeStatic( );
AFFINE_VIEWPORT_OVERLAY_WIDGET export const noteSlicerWidget = WidgetViewExtension(
)}`, 'affine:page',
[AFFINE_EDGELESS_AUTO_CONNECT_WIDGET]: literal`${unsafeStatic( NOTE_SLICER_WIDGET,
AFFINE_EDGELESS_AUTO_CONNECT_WIDGET literal`${unsafeStatic(NOTE_SLICER_WIDGET)}`
)}`, );
[AFFINE_SCROLL_ANCHORING_WIDGET]: literal`${unsafeStatic(AFFINE_SCROLL_ANCHORING_WIDGET)}`, export const edgelessNavigatorBlackBackgroundWidget = WidgetViewExtension(
[EDGELESS_DRAGGING_AREA_WIDGET]: literal`${unsafeStatic(EDGELESS_DRAGGING_AREA_WIDGET)}`, 'affine:page',
[NOTE_SLICER_WIDGET]: literal`${unsafeStatic(NOTE_SLICER_WIDGET)}`, EDGELESS_NAVIGATOR_BLACK_BACKGROUND_WIDGET,
[EDGELESS_NAVIGATOR_BLACK_BACKGROUND_WIDGET]: literal`${unsafeStatic(EDGELESS_NAVIGATOR_BLACK_BACKGROUND_WIDGET)}`, literal`${unsafeStatic(EDGELESS_NAVIGATOR_BLACK_BACKGROUND_WIDGET)}`
[EDGELESS_SELECTED_RECT_WIDGET]: literal`${unsafeStatic(EDGELESS_SELECTED_RECT_WIDGET)}`, );
[EDGELESS_TOOLBAR_WIDGET]: literal`${unsafeStatic(EDGELESS_TOOLBAR_WIDGET)}`, export const edgelessSelectedRectWidget = WidgetViewExtension(
}; 'affine:page',
EDGELESS_SELECTED_RECT_WIDGET,
const EdgelessCommonExtension: ExtensionType[] = [ literal`${unsafeStatic(EDGELESS_SELECTED_RECT_WIDGET)}`
FlavourExtension('affine:page'), );
EdgelessRootService, export const edgelessToolbarWidget = WidgetViewExtension(
DocModeService, 'affine:page',
ThemeService, EDGELESS_TOOLBAR_WIDGET,
EmbedOptionService, literal`${unsafeStatic(EDGELESS_TOOLBAR_WIDGET)}`
ExportManagerExtension, );
ToolController,
DNDAPIExtension,
PageViewportServiceExtension,
RootBlockAdapterExtensions,
FileDropExtension,
].flat();
export const EdgelessRootBlockSpec: ExtensionType[] = [
...EdgelessCommonExtension,
BlockViewExtension('affine:page', literal`affine-edgeless-root`),
WidgetViewMapExtension('affine:page', edgelessRootWidgetViewMap),
];
class EdgelessLocker extends BlockServiceWatcher { class EdgelessLocker extends BlockServiceWatcher {
static override readonly flavour = 'affine:page'; static override readonly flavour = 'affine:page';
@@ -119,6 +85,27 @@ class EdgelessLocker extends BlockServiceWatcher {
} }
} }
const EdgelessCommonExtension: ExtensionType[] = [
CommonSpecs,
ToolController,
EdgelessRootService,
].flat();
export const EdgelessRootBlockSpec: ExtensionType[] = [
...EdgelessCommonExtension,
BlockViewExtension('affine:page', literal`affine-edgeless-root`),
edgelessRemoteSelectionWidget,
edgelessZoomToolbarWidget,
frameTitleWidget,
elementToolbarWidget,
autoConnectWidget,
edgelessDraggingAreaWidget,
noteSlicerWidget,
edgelessNavigatorBlackBackgroundWidget,
edgelessSelectedRectWidget,
edgelessToolbarWidget,
];
export const PreviewEdgelessRootBlockSpec: ExtensionType[] = [ export const PreviewEdgelessRootBlockSpec: ExtensionType[] = [
...EdgelessCommonExtension, ...EdgelessCommonExtension,
BlockViewExtension('affine:page', literal`affine-edgeless-root-preview`), BlockViewExtension('affine:page', literal`affine-edgeless-root-preview`),

View File

@@ -1,5 +1,6 @@
export * from './adapters/markdown.js'; export * from './adapters/markdown.js';
export * from './clipboard/index.js'; export * from './clipboard/index.js';
export * from './common-specs/index.js';
export * from './edgeless/edgeless-root-spec.js'; export * from './edgeless/edgeless-root-spec.js';
export * from './edgeless/index.js'; export * from './edgeless/index.js';
export { TemplateJob } from './edgeless/services/template.js'; export { TemplateJob } from './edgeless/services/template.js';

View File

@@ -1,83 +1,34 @@
import { FileDropExtension } from '@blocksuite/affine-components/drop-indicator'; import { BlockViewExtension, WidgetViewExtension } from '@blocksuite/block-std';
import {
DNDAPIExtension,
DocModeService,
EmbedOptionService,
PageViewportServiceExtension,
ThemeService,
} from '@blocksuite/affine-shared/services';
import { AFFINE_DRAG_HANDLE_WIDGET } from '@blocksuite/affine-widget-drag-handle';
import { AFFINE_DOC_REMOTE_SELECTION_WIDGET } from '@blocksuite/affine-widget-remote-selection';
import { AFFINE_SCROLL_ANCHORING_WIDGET } from '@blocksuite/affine-widget-scroll-anchoring';
import {
BlockViewExtension,
FlavourExtension,
WidgetViewMapExtension,
} from '@blocksuite/block-std';
import type { ExtensionType } from '@blocksuite/store'; import type { ExtensionType } from '@blocksuite/store';
import { literal, unsafeStatic } from 'lit/static-html.js'; import { literal, unsafeStatic } from 'lit/static-html.js';
import { ExportManagerExtension } from '../../_common/export-manager/export-manager.js'; import { CommonSpecs } from '../common-specs/index.js';
import { RootBlockAdapterExtensions } from '../adapters/extension.js';
import { AFFINE_EMBED_CARD_TOOLBAR_WIDGET } from '../widgets/embed-card-toolbar/embed-card-toolbar.js';
import { AFFINE_FORMAT_BAR_WIDGET } from '../widgets/format-bar/format-bar.js';
import { AFFINE_INNER_MODAL_WIDGET } from '../widgets/inner-modal/inner-modal.js';
import { AFFINE_KEYBOARD_TOOLBAR_WIDGET } from '../widgets/keyboard-toolbar/index.js'; import { AFFINE_KEYBOARD_TOOLBAR_WIDGET } from '../widgets/keyboard-toolbar/index.js';
import { AFFINE_LINKED_DOC_WIDGET } from '../widgets/linked-doc/config.js';
import { AFFINE_MODAL_WIDGET } from '../widgets/modal/modal.js';
import { AFFINE_PAGE_DRAGGING_AREA_WIDGET } from '../widgets/page-dragging-area/page-dragging-area.js'; import { AFFINE_PAGE_DRAGGING_AREA_WIDGET } from '../widgets/page-dragging-area/page-dragging-area.js';
import { AFFINE_SLASH_MENU_WIDGET } from '../widgets/slash-menu/index.js';
import { AFFINE_VIEWPORT_OVERLAY_WIDGET } from '../widgets/viewport-overlay/viewport-overlay.js';
import { PageRootService } from './page-root-service.js'; import { PageRootService } from './page-root-service.js';
export const pageRootWidgetViewMap = { export const keyboardToolbarWidget = WidgetViewExtension(
[AFFINE_KEYBOARD_TOOLBAR_WIDGET]: literal`${unsafeStatic(AFFINE_KEYBOARD_TOOLBAR_WIDGET)}`, 'affine:page',
[AFFINE_MODAL_WIDGET]: literal`${unsafeStatic(AFFINE_MODAL_WIDGET)}`, AFFINE_KEYBOARD_TOOLBAR_WIDGET,
[AFFINE_INNER_MODAL_WIDGET]: literal`${unsafeStatic(AFFINE_INNER_MODAL_WIDGET)}`, literal`${unsafeStatic(AFFINE_KEYBOARD_TOOLBAR_WIDGET)}`
[AFFINE_SLASH_MENU_WIDGET]: literal`${unsafeStatic( );
AFFINE_SLASH_MENU_WIDGET
)}`, export const pageDraggingAreaWidget = WidgetViewExtension(
[AFFINE_LINKED_DOC_WIDGET]: literal`${unsafeStatic( 'affine:page',
AFFINE_LINKED_DOC_WIDGET AFFINE_PAGE_DRAGGING_AREA_WIDGET,
)}`, literal`${unsafeStatic(AFFINE_PAGE_DRAGGING_AREA_WIDGET)}`
[AFFINE_DRAG_HANDLE_WIDGET]: literal`${unsafeStatic( );
AFFINE_DRAG_HANDLE_WIDGET
)}`,
[AFFINE_EMBED_CARD_TOOLBAR_WIDGET]: literal`${unsafeStatic(
AFFINE_EMBED_CARD_TOOLBAR_WIDGET
)}`,
[AFFINE_FORMAT_BAR_WIDGET]: literal`${unsafeStatic(
AFFINE_FORMAT_BAR_WIDGET
)}`,
[AFFINE_DOC_REMOTE_SELECTION_WIDGET]: literal`${unsafeStatic(
AFFINE_DOC_REMOTE_SELECTION_WIDGET
)}`,
[AFFINE_PAGE_DRAGGING_AREA_WIDGET]: literal`${unsafeStatic(
AFFINE_PAGE_DRAGGING_AREA_WIDGET
)}`,
[AFFINE_VIEWPORT_OVERLAY_WIDGET]: literal`${unsafeStatic(
AFFINE_VIEWPORT_OVERLAY_WIDGET
)}`,
[AFFINE_SCROLL_ANCHORING_WIDGET]: literal`${unsafeStatic(AFFINE_SCROLL_ANCHORING_WIDGET)}`,
};
const PageCommonExtension: ExtensionType[] = [ const PageCommonExtension: ExtensionType[] = [
FlavourExtension('affine:page'), CommonSpecs,
PageRootService, PageRootService,
DocModeService, pageDraggingAreaWidget,
ThemeService, ].flat();
EmbedOptionService,
PageViewportServiceExtension,
];
export const PageRootBlockSpec: ExtensionType[] = [ export const PageRootBlockSpec: ExtensionType[] = [
...PageCommonExtension, ...PageCommonExtension,
BlockViewExtension('affine:page', literal`affine-page-root`), BlockViewExtension('affine:page', literal`affine-page-root`),
WidgetViewMapExtension('affine:page', pageRootWidgetViewMap), keyboardToolbarWidget,
ExportManagerExtension,
DNDAPIExtension,
RootBlockAdapterExtensions,
FileDropExtension,
].flat(); ].flat();
export const PreviewPageRootBlockSpec: ExtensionType[] = [ export const PreviewPageRootBlockSpec: ExtensionType[] = [

View File

@@ -1,32 +1,40 @@
import type { ExtensionType } from '@blocksuite/store'; import type { ExtensionType } from '@blocksuite/store';
import { WidgetViewMapIdentifier } from '../identifier.js'; import { WidgetViewIdentifier } from '../identifier.js';
import type { WidgetViewMapType } from '../spec/type.js'; import type { WidgetViewType } from '../spec/type.js';
/** /**
* Create a widget view map extension. * Create a widget view extension.
* *
* @param flavour The flavour of the block that the widget view map is for. * @param flavour The flavour of the block that the widget view is for.
* @param widgetViewMap A map of widget names to widget view lit literal. * @param id The id of the widget view.
* @param view The widget view lit literal.
* *
* A widget view map is to provide a map of widgets to a block. * A widget view is to provide a widget view for a block.
* For every target block, it's view will be rendered with the widget views. * For every target block, it's view will be rendered with the widget view.
* *
* @example * @example
* ```ts * ```ts
* import { WidgetViewMapExtension } from '@blocksuite/block-std'; * import { WidgetViewExtension } from '@blocksuite/block-std';
* *
* const MyWidgetViewMapExtension = WidgetViewMapExtension('my-flavour', { * const MyWidgetViewExtension = WidgetViewExtension('my-flavour', 'my-widget', literal`my-widget-view`);
* 'my-widget': literal`my-widget-view`
* });
*/ */
export function WidgetViewMapExtension( export function WidgetViewExtension(
flavour: string, flavour: string,
widgetViewMap: WidgetViewMapType id: string,
view: WidgetViewType
): ExtensionType { ): ExtensionType {
return { return {
setup: di => { setup: di => {
di.addImpl(WidgetViewMapIdentifier(flavour), () => widgetViewMap); if (flavour.includes('|') || id.includes('|')) {
console.error(`Register view failed:`);
console.error(
`flavour or id cannot include '|', flavour: ${flavour}, id: ${id}`
);
return;
}
const key = `${flavour}|${id}`;
di.addImpl(WidgetViewIdentifier(key), view);
}, },
}; };
} }

View File

@@ -4,7 +4,7 @@ import type { Command } from './command/index.js';
import type { EventOptions, UIEventHandler } from './event/index.js'; import type { EventOptions, UIEventHandler } from './event/index.js';
import type { BlockService, LifeCycleWatcher } from './extension/index.js'; import type { BlockService, LifeCycleWatcher } from './extension/index.js';
import type { BlockStdScope } from './scope/index.js'; import type { BlockStdScope } from './scope/index.js';
import type { BlockViewType, WidgetViewMapType } from './spec/type.js'; import type { BlockViewType, WidgetViewType } from './spec/type.js';
export const BlockServiceIdentifier = export const BlockServiceIdentifier =
createIdentifier<BlockService>('BlockService'); createIdentifier<BlockService>('BlockService');
@@ -20,8 +20,8 @@ export const ConfigIdentifier =
export const BlockViewIdentifier = createIdentifier<BlockViewType>('BlockView'); export const BlockViewIdentifier = createIdentifier<BlockViewType>('BlockView');
export const WidgetViewMapIdentifier = export const WidgetViewIdentifier =
createIdentifier<WidgetViewMapType>('WidgetViewMap'); createIdentifier<WidgetViewType>('WidgetView');
export const LifeCycleWatcherIdentifier = export const LifeCycleWatcherIdentifier =
createIdentifier<LifeCycleWatcher>('LifeCycleWatcher'); createIdentifier<LifeCycleWatcher>('LifeCycleWatcher');

View File

@@ -2,4 +2,4 @@ import type { BlockModel } from '@blocksuite/store';
import type { StaticValue } from 'lit/static-html.js'; import type { StaticValue } from 'lit/static-html.js';
export type BlockViewType = StaticValue | ((model: BlockModel) => StaticValue); export type BlockViewType = StaticValue | ((model: BlockModel) => StaticValue);
export type WidgetViewMapType = Record<string, StaticValue>; export type WidgetViewType = StaticValue;

View File

@@ -17,7 +17,7 @@ import { html, type StaticValue, unsafeStatic } from 'lit/static-html.js';
import type { CommandManager } from '../../command/index.js'; import type { CommandManager } from '../../command/index.js';
import type { UIEventDispatcher } from '../../event/index.js'; import type { UIEventDispatcher } from '../../event/index.js';
import { WidgetViewMapIdentifier } from '../../identifier.js'; import { WidgetViewIdentifier } from '../../identifier.js';
import type { RangeManager } from '../../range/index.js'; import type { RangeManager } from '../../range/index.js';
import type { BlockStdScope } from '../../scope/block-std-scope.js'; import type { BlockStdScope } from '../../scope/block-std-scope.js';
import { PropTypes, requiredProperties } from '../decorators/index.js'; import { PropTypes, requiredProperties } from '../decorators/index.js';
@@ -56,22 +56,21 @@ export class EditorHost extends SignalWatcher(
console.warn(`Cannot find render flavour ${flavour}.`); console.warn(`Cannot find render flavour ${flavour}.`);
return html`${nothing}`; return html`${nothing}`;
} }
const widgetViewMap = this.std.getOptional(
WidgetViewMapIdentifier(flavour) const widgetViews = this.std.provider.getAll(WidgetViewIdentifier);
const widgets = widgetViews.entries().reduce(
(mapping, [key, tag]) => {
const [widgetFlavour, id] = key.split('|');
if (widgetFlavour === flavour) {
const template = html`<${tag} ${unsafeStatic(WIDGET_ID_ATTR)}=${id}></${tag}>`;
mapping[id] = template;
}
return mapping;
},
{} as Record<string, TemplateResult>
); );
const tag = typeof view === 'function' ? view(model) : view; const tag = typeof view === 'function' ? view(model) : view;
const widgets: Record<string, TemplateResult> = widgetViewMap
? Object.entries(widgetViewMap).reduce((mapping, [key, tag]) => {
const template = html`<${tag} ${unsafeStatic(WIDGET_ID_ATTR)}=${key}></${tag}>`;
return {
...mapping,
[key]: template,
};
}, {})
: {};
return html`<${tag} return html`<${tag}
${unsafeStatic(BLOCK_ID_ATTR)}=${model.id} ${unsafeStatic(BLOCK_ID_ATTR)}=${model.id}
.widgets=${widgets} .widgets=${widgets}
@@ -144,13 +143,22 @@ export class EditorHost extends SignalWatcher(
const view = this.std.getView(rootModel.flavour); const view = this.std.getView(rootModel.flavour);
if (!view) return result; if (!view) return result;
const widgetViewMap = this.std.getOptional( const widgetViews = this.std.provider.getAll(
WidgetViewMapIdentifier(rootModel.flavour) WidgetViewIdentifier(rootModel.flavour)
);
const widgetTags = Object.entries(widgetViews).reduce(
(mapping, [key, tag]) => {
const [widgetFlavour, id] = key.split('|');
if (widgetFlavour === rootModel.flavour) {
mapping[id] = tag;
}
return mapping;
},
{} as Record<string, StaticValue>
); );
const widgetTags = Object.values(widgetViewMap ?? {});
const elementsTags: StaticValue[] = [ const elementsTags: StaticValue[] = [
typeof view === 'function' ? view(rootModel) : view, typeof view === 'function' ? view(rootModel) : view,
...widgetTags, ...Object.values(widgetTags),
]; ];
await Promise.all( await Promise.all(
elementsTags.map(tag => { elementsTags.map(tag => {

View File

@@ -1,9 +1,6 @@
import '../../style.css'; import '../../style.css';
import { import * as blockStd from '@blocksuite/block-std';
WidgetViewMapExtension,
WidgetViewMapIdentifier,
} from '@blocksuite/block-std';
import * as blocks from '@blocksuite/blocks'; import * as blocks from '@blocksuite/blocks';
import { import {
CommunityCanvasTextFonts, CommunityCanvasTextFonts,
@@ -52,8 +49,8 @@ async function main() {
blocks, blocks,
global: { utils: globalUtils }, global: { utils: globalUtils },
editor, editor,
blockStd: blockStd,
identifiers: { identifiers: {
WidgetViewMapIdentifier,
QuickSearchProvider, QuickSearchProvider,
DocModeProvider, DocModeProvider,
RefNodeSlotsProvider, RefNodeSlotsProvider,
@@ -65,7 +62,6 @@ async function main() {
], ],
extensions: { extensions: {
FontConfigExtension: FontConfigExtension(CommunityCanvasTextFonts), FontConfigExtension: FontConfigExtension(CommunityCanvasTextFonts),
WidgetViewMapExtension,
}, },
mockServices: { mockServices: {
mockDocModeService, mockDocModeService,

View File

@@ -794,13 +794,11 @@ test.describe('slash menu with customize menu', () => {
{ {
setup: di => { setup: di => {
di.override( di.override(
window.$blocksuite.identifiers.WidgetViewMapIdentifier( window.$blocksuite.blockStd.WidgetViewIdentifier(
'affine:page' 'affine:page|affine-slash-menu-widget'
), ),
// @ts-ignore // @ts-ignore
() => ({ fakeLiteral`affine-custom-slash-menu`
'affine-slash-menu-widget': fakeLiteral`affine-custom-slash-menu`,
})
); );
}, },
}, },
@@ -869,13 +867,11 @@ test.describe('slash menu with customize menu', () => {
{ {
setup: di => setup: di =>
di.override( di.override(
window.$blocksuite.identifiers.WidgetViewMapIdentifier( window.$blocksuite.blockStd.WidgetViewIdentifier(
'affine:page' 'affine:page|affine-slash-menu-widget'
), ),
// @ts-ignore // @ts-ignore
() => ({ fakeLiteral`affine-custom-slash-menu`
'affine-slash-menu-widget': fakeLiteral`affine-custom-slash-menu`,
})
), ),
}, },
]; ];

View File

@@ -5,11 +5,7 @@ import type {
QuickSearchProvider, QuickSearchProvider,
ThemeProvider, ThemeProvider,
} from '@blocksuite/affine-shared/services'; } from '@blocksuite/affine-shared/services';
import type { import type { EditorHost } from '@blocksuite/block-std';
EditorHost,
WidgetViewMapExtension,
WidgetViewMapIdentifier,
} from '@blocksuite/block-std';
import type { RefNodeSlotsProvider, TestUtils } from '@blocksuite/blocks'; import type { RefNodeSlotsProvider, TestUtils } from '@blocksuite/blocks';
import type { AffineEditorContainer } from '@blocksuite/presets'; import type { AffineEditorContainer } from '@blocksuite/presets';
import type { import type {
@@ -32,8 +28,8 @@ declare global {
utils: typeof import('@blocksuite/global/utils'); utils: typeof import('@blocksuite/global/utils');
}; };
editor: typeof import('@blocksuite/presets'); editor: typeof import('@blocksuite/presets');
blockStd: typeof import('@blocksuite/block-std');
identifiers: { identifiers: {
WidgetViewMapIdentifier: typeof WidgetViewMapIdentifier;
QuickSearchProvider: typeof QuickSearchProvider; QuickSearchProvider: typeof QuickSearchProvider;
DocModeProvider: typeof DocModeProvider; DocModeProvider: typeof DocModeProvider;
ThemeProvider: typeof ThemeProvider; ThemeProvider: typeof ThemeProvider;
@@ -41,9 +37,6 @@ declare global {
ParseDocUrlService: typeof ParseDocUrlProvider; ParseDocUrlService: typeof ParseDocUrlProvider;
}; };
defaultExtensions: () => ExtensionType[]; defaultExtensions: () => ExtensionType[];
extensions: {
WidgetViewMapExtension: typeof WidgetViewMapExtension;
};
mockServices: { mockServices: {
mockDocModeService: typeof DocModeService; mockDocModeService: typeof DocModeService;
}; };

View File

@@ -1,6 +1,6 @@
import { import {
BlockServiceWatcher, BlockServiceWatcher,
WidgetViewMapIdentifier, WidgetViewExtension,
} from '@blocksuite/affine/block-std'; } from '@blocksuite/affine/block-std';
import { import {
AFFINE_AI_PANEL_WIDGET, AFFINE_AI_PANEL_WIDGET,
@@ -14,10 +14,8 @@ import {
EdgelessCopilotWidget, EdgelessCopilotWidget,
EdgelessElementToolbarWidget, EdgelessElementToolbarWidget,
EdgelessRootBlockSpec, EdgelessRootBlockSpec,
edgelessRootWidgetViewMap,
ImageBlockSpec, ImageBlockSpec,
PageRootBlockSpec, PageRootBlockSpec,
pageRootWidgetViewMap,
ParagraphBlockService, ParagraphBlockService,
ParagraphBlockSpec, ParagraphBlockSpec,
} from '@blocksuite/affine/blocks'; } from '@blocksuite/affine/blocks';
@@ -63,25 +61,22 @@ function getAIPageRootWatcher(framework: FrameworkProvider) {
return AIPageRootWatcher; return AIPageRootWatcher;
} }
const aiPanelWidget = WidgetViewExtension(
'affine:page',
AFFINE_AI_PANEL_WIDGET,
literal`${unsafeStatic(AFFINE_AI_PANEL_WIDGET)}`
);
const edgelessCopilotWidget = WidgetViewExtension(
'affine:page',
AFFINE_EDGELESS_COPILOT_WIDGET,
literal`${unsafeStatic(AFFINE_EDGELESS_COPILOT_WIDGET)}`
);
export function createAIPageRootBlockSpec( export function createAIPageRootBlockSpec(
framework: FrameworkProvider framework: FrameworkProvider
): ExtensionType[] { ): ExtensionType[] {
return [ return [...PageRootBlockSpec, aiPanelWidget, getAIPageRootWatcher(framework)];
...PageRootBlockSpec,
getAIPageRootWatcher(framework),
{
setup: di => {
di.override(WidgetViewMapIdentifier('affine:page'), () => {
return {
...pageRootWidgetViewMap,
[AFFINE_AI_PANEL_WIDGET]: literal`${unsafeStatic(
AFFINE_AI_PANEL_WIDGET
)}`,
};
});
},
},
];
} }
function getAIEdgelessRootWatcher(framework: FrameworkProvider) { function getAIEdgelessRootWatcher(framework: FrameworkProvider) {
@@ -123,22 +118,9 @@ export function createAIEdgelessRootBlockSpec(
): ExtensionType[] { ): ExtensionType[] {
return [ return [
...EdgelessRootBlockSpec, ...EdgelessRootBlockSpec,
aiPanelWidget,
edgelessCopilotWidget,
getAIEdgelessRootWatcher(framework), getAIEdgelessRootWatcher(framework),
{
setup: di => {
di.override(WidgetViewMapIdentifier('affine:page'), () => {
return {
...edgelessRootWidgetViewMap,
[AFFINE_EDGELESS_COPILOT_WIDGET]: literal`${unsafeStatic(
AFFINE_EDGELESS_COPILOT_WIDGET
)}`,
[AFFINE_AI_PANEL_WIDGET]: literal`${unsafeStatic(
AFFINE_AI_PANEL_WIDGET
)}`,
};
});
},
},
]; ];
} }

View File

@@ -17,7 +17,15 @@ import { toURLSearchParams } from '@affine/core/modules/navigation';
import { PeekViewService } from '@affine/core/modules/peek-view/services/peek-view'; import { PeekViewService } from '@affine/core/modules/peek-view/services/peek-view';
import { WorkspaceService } from '@affine/core/modules/workspace'; import { WorkspaceService } from '@affine/core/modules/workspace';
import track from '@affine/track'; import track from '@affine/track';
import type { DocMode } from '@blocksuite/affine/blocks'; import {
codeToolbarWidget,
type DocMode,
embedCardToolbarWidget,
formatBarWidget,
imageToolbarWidget,
slashMenuWidget,
surfaceRefToolbarWidget,
} from '@blocksuite/affine/blocks';
import { import {
DocTitle, DocTitle,
EdgelessEditor, EdgelessEditor,
@@ -173,6 +181,12 @@ const usePatchSpecs = (mode: DocMode) => {
builder.extend([patchForAttachmentEmbedViews(reactToLit)]); builder.extend([patchForAttachmentEmbedViews(reactToLit)]);
} }
if (BUILD_CONFIG.isMobileEdition) { if (BUILD_CONFIG.isMobileEdition) {
builder.omit(formatBarWidget);
builder.omit(embedCardToolbarWidget);
builder.omit(slashMenuWidget);
builder.omit(codeToolbarWidget);
builder.omit(imageToolbarWidget);
builder.omit(surfaceRefToolbarWidget);
builder.extend([patchForMobile()].flat()); builder.extend([patchForMobile()].flat());
} }
if (BUILD_CONFIG.isElectron) { if (BUILD_CONFIG.isElectron) {