diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/edgeless-root-service.ts b/blocksuite/affine/blocks/block-root/src/edgeless/edgeless-root-service.ts index cef30fe3fb..69d8ec4fab 100644 --- a/blocksuite/affine/blocks/block-root/src/edgeless/edgeless-root-service.ts +++ b/blocksuite/affine/blocks/block-root/src/edgeless/edgeless-root-service.ts @@ -2,8 +2,6 @@ import { EdgelessFrameManagerIdentifier } from '@blocksuite/affine-block-frame'; import { EdgelessCRUDIdentifier, EdgelessLegacySlotIdentifier, - type ElementRenderer, - elementRenderers, getSurfaceBlock, type SurfaceBlockModel, type SurfaceContext, @@ -48,8 +46,6 @@ export class EdgelessRootService extends RootService implements SurfaceContext { private readonly _surface: SurfaceBlockModel; - elementRenderers: Record = elementRenderers; - TemplateJob = TemplateJob; get blocks(): GfxBlockElementModel[] { diff --git a/blocksuite/affine/blocks/block-surface/src/extensions/element-renderer.ts b/blocksuite/affine/blocks/block-surface/src/extensions/element-renderer.ts new file mode 100644 index 0000000000..62d644faaa --- /dev/null +++ b/blocksuite/affine/blocks/block-surface/src/extensions/element-renderer.ts @@ -0,0 +1,31 @@ +import { + createIdentifier, + type ServiceIdentifier, +} from '@blocksuite/global/di'; +import type { + GfxLocalElementModel, + GfxPrimitiveElementModel, +} from '@blocksuite/std/gfx'; +import type { ExtensionType } from '@blocksuite/store'; + +import type { ElementRenderer } from '../renderer/elements'; + +export const ElementRendererIdentifier = + createIdentifier('element-renderer'); + +export const ElementRendererExtension = < + T extends GfxPrimitiveElementModel | GfxLocalElementModel, +>( + id: string, + renderer: ElementRenderer +): ExtensionType & { + identifier: ServiceIdentifier>; +} => { + const identifier = ElementRendererIdentifier>(id); + return { + setup: di => { + di.addImpl(identifier, () => renderer); + }, + identifier, + }; +}; diff --git a/blocksuite/affine/blocks/block-surface/src/extensions/index.ts b/blocksuite/affine/blocks/block-surface/src/extensions/index.ts index 547c4122a7..99ba393097 100644 --- a/blocksuite/affine/blocks/block-surface/src/extensions/index.ts +++ b/blocksuite/affine/blocks/block-surface/src/extensions/index.ts @@ -1,5 +1,6 @@ export * from './clipboard-config'; export * from './crud-extension'; +export * from './element-renderer'; export * from './export-manager'; export * from './legacy-slot-extension'; export * from './query'; diff --git a/blocksuite/affine/blocks/block-surface/src/index.ts b/blocksuite/affine/blocks/block-surface/src/index.ts index fa4199df2a..e37b072b8e 100644 --- a/blocksuite/affine/blocks/block-surface/src/index.ts +++ b/blocksuite/affine/blocks/block-surface/src/index.ts @@ -22,10 +22,7 @@ export { export { CanvasRenderer } from './renderer/canvas-renderer.js'; export * from './renderer/elements/group/consts.js'; export type { ElementRenderer } from './renderer/elements/index.js'; -export { - elementRenderers, - normalizeShapeBound, -} from './renderer/elements/index.js'; +export { normalizeShapeBound } from './renderer/elements/index.js'; export { fitContent } from './renderer/elements/shape/utils.js'; export * from './renderer/elements/type.js'; export { Overlay, OverlayIdentifier } from './renderer/overlay.js'; diff --git a/blocksuite/affine/blocks/block-surface/src/renderer/canvas-renderer.ts b/blocksuite/affine/blocks/block-surface/src/renderer/canvas-renderer.ts index df17e7bb6d..476a4dafa0 100644 --- a/blocksuite/affine/blocks/block-surface/src/renderer/canvas-renderer.ts +++ b/blocksuite/affine/blocks/block-surface/src/renderer/canvas-renderer.ts @@ -3,6 +3,7 @@ import { requestConnectedFrame } from '@blocksuite/affine-shared/utils'; import { DisposableGroup } from '@blocksuite/global/disposable'; import type { IBound } from '@blocksuite/global/gfx'; import { getBoundWithRotation, intersects } from '@blocksuite/global/gfx'; +import type { BlockStdScope } from '@blocksuite/std'; import type { GridManager, LayerManager, @@ -13,6 +14,7 @@ import last from 'lodash-es/last'; import { Subject } from 'rxjs'; import type { SurfaceElementModel } from '../element-model/base.js'; +import { ElementRendererIdentifier } from '../extensions/element-renderer.js'; import { RoughCanvas } from '../utils/rough/canvas.js'; import type { ElementRenderer } from './elements/index.js'; import type { Overlay } from './overlay.js'; @@ -26,12 +28,12 @@ type EnvProvider = { }; type RendererOptions = { + std: BlockStdScope; viewport: Viewport; layerManager: LayerManager; provider?: Partial; enableStackingCanvas?: boolean; onStackingCanvasCreated?: (canvas: HTMLCanvasElement) => void; - elementRenderers: Record; gridManager: GridManager; surfaceModel: SurfaceBlockModel; }; @@ -51,7 +53,7 @@ export class CanvasRenderer { ctx: CanvasRenderingContext2D; - elementRenderers: Record; + std: BlockStdScope; grid: GridManager; @@ -76,11 +78,11 @@ export class CanvasRenderer { this.canvas = canvas; this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D; + this.std = options.std; this.viewport = options.viewport; this.layerManager = options.layerManager; this.grid = options.gridManager; this.provider = options.provider ?? {}; - this.elementRenderers = options.elementRenderers; this._initViewport(); options.enableStackingCanvas = options.enableStackingCanvas ?? false; @@ -277,10 +279,9 @@ export class CanvasRenderer { for (const element of elements) { const display = (element.display ?? true) && !element.hidden; if (display && intersects(getBoundWithRotation(element), bound)) { - const renderFn = - this.elementRenderers[ - element.type as keyof typeof this.elementRenderers - ]; + const renderFn = this.std.getOptional( + ElementRendererIdentifier(element.type) + ); if (!renderFn) { console.warn(`Cannot find renderer for ${element.type}`); diff --git a/blocksuite/affine/blocks/block-surface/src/renderer/elements/index.ts b/blocksuite/affine/blocks/block-surface/src/renderer/elements/index.ts index 1d6754b463..a5794675e5 100644 --- a/blocksuite/affine/blocks/block-surface/src/renderer/elements/index.ts +++ b/blocksuite/affine/blocks/block-surface/src/renderer/elements/index.ts @@ -1,6 +1,10 @@ import type { IBound } from '@blocksuite/global/gfx'; -import type { GfxPrimitiveElementModel } from '@blocksuite/std/gfx'; +import type { + GfxLocalElementModel, + GfxPrimitiveElementModel, +} from '@blocksuite/std/gfx'; +import { ElementRendererExtension } from '../../extensions/element-renderer.js'; import type { RoughCanvas } from '../../index.js'; import type { CanvasRenderer } from '../canvas-renderer.js'; import { brush } from './brush/index.js'; @@ -13,7 +17,9 @@ import { text } from './text/index.js'; export { normalizeShapeBound } from './shape/utils.js'; export type ElementRenderer< - T extends GfxPrimitiveElementModel = GfxPrimitiveElementModel, + T extends + | GfxPrimitiveElementModel + | GfxLocalElementModel = GfxPrimitiveElementModel, > = ( model: T, ctx: CanvasRenderingContext2D, @@ -23,12 +29,47 @@ export type ElementRenderer< viewportBound: IBound ) => void; -export const elementRenderers = { - brush, - highlighter, - connector, - group, - shape, - text, - mindmap, -} as Record>; +export const BrushElementRendererExtension = ElementRendererExtension( + 'brush', + brush +); + +export const HighlighterElementRendererExtension = ElementRendererExtension( + 'highlighter', + highlighter +); + +export const ConnectorElementRendererExtension = ElementRendererExtension( + 'connector', + connector +); + +export const GroupElementRendererExtension = ElementRendererExtension( + 'group', + group +); + +export const ShapeElementRendererExtension = ElementRendererExtension( + 'shape', + shape +); + +export const TextElementRendererExtension = ElementRendererExtension( + 'text', + text +); + +export const MindmapElementRendererExtension = ElementRendererExtension( + 'mindmap', + mindmap +); + +export const elementRendererExtensions = [ + BrushElementRendererExtension, + HighlighterElementRendererExtension, + ConnectorElementRendererExtension, + GroupElementRendererExtension, + ShapeElementRendererExtension, + TextElementRendererExtension, + MindmapElementRendererExtension, +]; diff --git a/blocksuite/affine/blocks/block-surface/src/surface-block.ts b/blocksuite/affine/blocks/block-surface/src/surface-block.ts index 66a398d250..d699d2ad96 100644 --- a/blocksuite/affine/blocks/block-surface/src/surface-block.ts +++ b/blocksuite/affine/blocks/block-surface/src/surface-block.ts @@ -11,17 +11,12 @@ import type { Subject } from 'rxjs'; import { ConnectorElementModel } from './element-model/index.js'; import { CanvasRenderer } from './renderer/canvas-renderer.js'; -import { - type ElementRenderer, - elementRenderers, -} from './renderer/elements/index.js'; import { OverlayIdentifier } from './renderer/overlay.js'; import type { SurfaceBlockModel } from './surface-model.js'; export interface SurfaceContext { viewport: Viewport; host: EditorHost; - elementRenderers: Record; selection: { selectedIds: string[]; slots: { @@ -148,6 +143,7 @@ export class SurfaceBlockComponent extends BlockComponent { const themeService = this.std.get(ThemeProvider); this._renderer = new CanvasRenderer({ + std: this.std, viewport: gfx.viewport, layerManager: gfx.layer, gridManager: gfx.grid, @@ -177,7 +173,6 @@ export class SurfaceBlockComponent extends BlockComponent { onStackingCanvasCreated(canvas) { canvas.className = 'indexable-canvas'; }, - elementRenderers, surfaceModel: this.model, }); diff --git a/blocksuite/affine/blocks/block-surface/src/surface-spec.ts b/blocksuite/affine/blocks/block-surface/src/surface-spec.ts index 8bdbf0449e..8f57a6414c 100644 --- a/blocksuite/affine/blocks/block-surface/src/surface-spec.ts +++ b/blocksuite/affine/blocks/block-surface/src/surface-spec.ts @@ -11,12 +11,14 @@ import { EdgelessLegacySlotExtension, } from './extensions'; import { ExportManagerExtension } from './extensions/export-manager/export-manager'; +import { elementRendererExtensions } from './renderer/elements'; const CommonSurfaceBlockSpec: ExtensionType[] = [ FlavourExtension('affine:surface'), EdgelessCRUDExtension, EdgelessLegacySlotExtension, ExportManagerExtension, + ...elementRendererExtensions, ]; export const PageSurfaceBlockSpec: ExtensionType[] = [ diff --git a/packages/frontend/core/src/blocksuite/ai/mini-mindmap/surface-block.ts b/packages/frontend/core/src/blocksuite/ai/mini-mindmap/surface-block.ts index 3147c374cc..d451325dc0 100644 --- a/packages/frontend/core/src/blocksuite/ai/mini-mindmap/surface-block.ts +++ b/packages/frontend/core/src/blocksuite/ai/mini-mindmap/surface-block.ts @@ -1,7 +1,6 @@ /* oxlint-disable @typescript-eslint/no-non-null-assertion */ import { CanvasRenderer, - elementRenderers, fitContent, type SurfaceBlockModel, } from '@blocksuite/affine/blocks/surface'; @@ -98,6 +97,7 @@ export class MindmapSurfaceBlock extends BlockComponent { const themeService = this.std.get(ThemeProvider); this.renderer = new CanvasRenderer({ + std: this.std, viewport: this.viewport, layerManager: this._layer, gridManager: this._grid, @@ -124,7 +124,6 @@ export class MindmapSurfaceBlock extends BlockComponent { themeService.edgelessTheme ), }, - elementRenderers, surfaceModel: this.model, }); this._disposables.add(this.renderer);