diff --git a/blocksuite/affine/block-surface/src/commands/auto-align.ts b/blocksuite/affine/block-surface/src/commands/auto-align.ts index 477a3b5790..68cd786f84 100644 --- a/blocksuite/affine/block-surface/src/commands/auto-align.ts +++ b/blocksuite/affine/block-surface/src/commands/auto-align.ts @@ -5,7 +5,10 @@ import { MindmapElementModel, NoteBlockModel, } from '@blocksuite/affine-model'; -import type { GfxModel } from '@blocksuite/block-std/gfx'; +import { + GfxControllerIdentifier, + type GfxModel, +} from '@blocksuite/block-std/gfx'; import { Bound } from '@blocksuite/global/utils'; import chunk from 'lodash.chunk'; @@ -15,6 +18,7 @@ const ALIGN_PADDING = 20; import type { Command } from '@blocksuite/block-std'; import type { BlockModel, BlockProps } from '@blocksuite/store'; +import { EdgelessCRUDIdentifier } from '../extensions/crud-extension.js'; import { updateXYWH } from '../utils/update-xywh.js'; /** @@ -25,11 +29,10 @@ export const autoArrangeElementsCommand: Command = ( next ) => { const { updateBlock } = ctx.std.doc; - const rootService = ctx.std.getService('affine:page'); - // @ts-expect-error TODO: fix after edgeless refactor - const elements = rootService?.selection.selectedElements; - // @ts-expect-error TODO: fix after edgeless refactor - const updateElement = rootService?.updateElement; + const gfx = ctx.std.get(GfxControllerIdentifier); + + const elements = gfx.selection.selectedElements; + const { updateElement } = ctx.std.get(EdgelessCRUDIdentifier); if (elements && updateElement) { autoArrangeElements(elements, updateElement, updateBlock); } @@ -44,11 +47,10 @@ export const autoResizeElementsCommand: Command = ( next ) => { const { updateBlock } = ctx.std.doc; - const rootService = ctx.std.getService('affine:page'); - // @ts-expect-error TODO: fix after edgeless refactor - const elements = rootService?.selection.selectedElements; - // @ts-expect-error TODO: fix after edgeless refactor - const updateElement = rootService?.updateElement; + const gfx = ctx.std.get(GfxControllerIdentifier); + + const elements = gfx.selection.selectedElements; + const { updateElement } = ctx.std.get(EdgelessCRUDIdentifier); if (elements && updateElement) { autoResizeElements(elements, updateElement, updateBlock); } diff --git a/blocksuite/affine/block-surface/src/extensions/crud-extension.ts b/blocksuite/affine/block-surface/src/extensions/crud-extension.ts new file mode 100644 index 0000000000..1c6e57d0d7 --- /dev/null +++ b/blocksuite/affine/block-surface/src/extensions/crud-extension.ts @@ -0,0 +1,140 @@ +import { EditPropsStore } from '@blocksuite/affine-shared/services'; +import { + type BlockStdScope, + Extension, + StdIdentifier, +} from '@blocksuite/block-std'; +import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx'; +import { type Container, createIdentifier } from '@blocksuite/global/di'; +import type { BlockModel } from '@blocksuite/store'; + +import type { SurfaceBlockModel } from '../surface-model'; +import { getLastPropsKey } from '../utils/get-last-props-key'; +import { isConnectable, isNoteBlock } from './query'; + +export const EdgelessCRUDIdentifier = createIdentifier( + 'AffineEdgelessCrudService' +); + +export class EdgelessCRUDExtension extends Extension { + constructor(readonly std: BlockStdScope) { + super(); + } + + static override setup(di: Container) { + di.add(this, [StdIdentifier]); + di.addImpl(EdgelessCRUDIdentifier, provider => provider.get(this)); + } + + private get _gfx() { + return this.std.get(GfxControllerIdentifier); + } + + private get _surface() { + return this._gfx.surface as SurfaceBlockModel | null; + } + + deleteElements(elements: BlockSuite.EdgelessModel[]) { + const surface = this._surface; + if (!surface) { + console.error('surface is not initialized'); + return; + } + + const gfx = this.std.get(GfxControllerIdentifier); + const set = new Set(elements); + elements.forEach(element => { + if (isConnectable(element)) { + const connectors = surface.getConnectors(element.id); + connectors.forEach(connector => set.add(connector)); + } + }); + + set.forEach(element => { + if (isNoteBlock(element)) { + const children = gfx.doc.root?.children ?? []; + if (children.length > 1) { + gfx.doc.deleteBlock(element); + } + } else { + gfx.deleteElement(element.id); + } + }); + } + + addBlock( + flavour: BlockSuite.EdgelessModelKeys | string, + props: Record, + parentId?: string | BlockModel, + parentIndex?: number + ) { + const gfx = this.std.get(GfxControllerIdentifier); + const key = getLastPropsKey(flavour as BlockSuite.EdgelessModelKeys, props); + if (key) { + props = this.std.get(EditPropsStore).applyLastProps(key, props); + } + + const nProps = { + ...props, + index: gfx.layer.generateIndex(), + }; + + return this.std.doc.addBlock( + flavour as never, + nProps, + parentId, + parentIndex + ); + } + + addElement>(type: string, props: T) { + const surface = this._surface; + if (!surface) { + console.error('surface is not initialized'); + return; + } + + const gfx = this.std.get(GfxControllerIdentifier); + const key = getLastPropsKey(type as BlockSuite.EdgelessModelKeys, props); + if (key) { + props = this.std.get(EditPropsStore).applyLastProps(key, props) as T; + } + + const nProps = { + ...props, + type, + index: props.index ?? gfx.layer.generateIndex(), + }; + + return surface.addElement(nProps); + } + + updateElement = (id: string, props: Record) => { + const surface = this._surface; + if (!surface) { + console.error('surface is not initialized'); + return; + } + + const element = this._surface.getElementById(id); + if (element) { + const key = getLastPropsKey( + element.type as BlockSuite.EdgelessModelKeys, + { ...element.yMap.toJSON(), ...props } + ); + key && this.std.get(EditPropsStore).recordLastProps(key, props); + this._surface.updateElement(id, props); + return; + } + + const block = this.std.doc.getBlockById(id); + if (block) { + const key = getLastPropsKey( + block.flavour as BlockSuite.EdgelessModelKeys, + { ...block.yBlock.toJSON(), ...props } + ); + key && this.std.get(EditPropsStore).recordLastProps(key, props); + this.std.doc.updateBlock(block, props); + } + }; +} diff --git a/blocksuite/affine/block-surface/src/extensions/index.ts b/blocksuite/affine/block-surface/src/extensions/index.ts new file mode 100644 index 0000000000..bfa3d9b634 --- /dev/null +++ b/blocksuite/affine/block-surface/src/extensions/index.ts @@ -0,0 +1 @@ +export * from './crud-extension'; diff --git a/blocksuite/affine/block-surface/src/extensions/query.ts b/blocksuite/affine/block-surface/src/extensions/query.ts new file mode 100644 index 0000000000..38ba97d295 --- /dev/null +++ b/blocksuite/affine/block-surface/src/extensions/query.ts @@ -0,0 +1,16 @@ +import type { NoteBlockModel } from '@blocksuite/affine-model'; +import type { BlockModel } from '@blocksuite/store'; + +import type { Connectable } from '../managers/connector-manager'; + +export function isConnectable( + element: BlockSuite.EdgelessModel | null +): element is Connectable { + return !!element && element.connectable; +} + +export function isNoteBlock( + element: BlockModel | BlockSuite.EdgelessModel | null +): element is NoteBlockModel { + return !!element && 'flavour' in element && element.flavour === 'affine:note'; +} diff --git a/blocksuite/affine/block-surface/src/index.ts b/blocksuite/affine/block-surface/src/index.ts index 4818187325..b15f8521c6 100644 --- a/blocksuite/affine/block-surface/src/index.ts +++ b/blocksuite/affine/block-surface/src/index.ts @@ -88,7 +88,11 @@ import { } from '@blocksuite/global/utils'; import { generateKeyBetween } from 'fractional-indexing'; -import { generateElementId, normalizeWheelDeltaY } from './utils/index.js'; +import { + generateElementId, + getLastPropsKey, + normalizeWheelDeltaY, +} from './utils'; import { addTree, containsNode, @@ -98,9 +102,11 @@ import { hideNodeConnector, moveNode, tryMoveNode, -} from './utils/mindmap/utils.js'; -export type { Options } from './utils/rough/core.js'; -export { sortIndex } from './utils/sort.js'; +} from './utils/mindmap/utils'; +export * from './extensions'; +export { getLastPropsKey } from './utils/get-last-props-key'; +export type { Options } from './utils/rough/core'; +export { sortIndex } from './utils/sort'; export { updateXYWH } from './utils/update-xywh.js'; export const ConnectorUtils = { @@ -129,6 +135,7 @@ export const CommonUtils = { getPointFromBoundsWithRotation, getStroke, getSvgPathFromStroke, + getLastPropsKey, intersects, isOverlap, isPointIn, diff --git a/blocksuite/affine/block-surface/src/surface-spec.ts b/blocksuite/affine/block-surface/src/surface-spec.ts index fc4f141ded..8e89e55e39 100644 --- a/blocksuite/affine/block-surface/src/surface-spec.ts +++ b/blocksuite/affine/block-surface/src/surface-spec.ts @@ -12,6 +12,7 @@ import { SurfaceBlockAdapterExtensions, } from './adapters/extension.js'; import { commands } from './commands/index.js'; +import { EdgelessCRUDExtension } from './extensions/crud-extension.js'; import { SurfaceBlockService } from './surface-service.js'; import { MindMapView } from './view/mindmap.js'; @@ -21,6 +22,7 @@ const CommonSurfaceBlockSpec: ExtensionType[] = [ CommandExtension(commands), HighlightSelectionExtension, MindMapView, + EdgelessCRUDExtension, ]; export const PageSurfaceBlockSpec: ExtensionType[] = [ diff --git a/blocksuite/blocks/src/root-block/edgeless/utils/get-last-props-key.ts b/blocksuite/affine/block-surface/src/utils/get-last-props-key.ts similarity index 100% rename from blocksuite/blocks/src/root-block/edgeless/utils/get-last-props-key.ts rename to blocksuite/affine/block-surface/src/utils/get-last-props-key.ts diff --git a/blocksuite/affine/block-surface/src/utils/index.ts b/blocksuite/affine/block-surface/src/utils/index.ts index c3df20e455..d3cb8efd03 100644 --- a/blocksuite/affine/block-surface/src/utils/index.ts +++ b/blocksuite/affine/block-surface/src/utils/index.ts @@ -32,3 +32,5 @@ export function normalizeWheelDeltaY(delta: number, zoom = 1) { Math.min(1, abs / 20); return newZoom; } + +export { getLastPropsKey } from './get-last-props-key'; diff --git a/blocksuite/blocks/src/_common/components/embed-card/modal/embed-card-create-modal.ts b/blocksuite/blocks/src/_common/components/embed-card/modal/embed-card-create-modal.ts index 5c226652c2..4e44c8ef9f 100644 --- a/blocksuite/blocks/src/_common/components/embed-card/modal/embed-card-create-modal.ts +++ b/blocksuite/blocks/src/_common/components/embed-card/modal/embed-card-create-modal.ts @@ -1,3 +1,4 @@ +import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface'; import { toast } from '@blocksuite/affine-components/toast'; import type { EmbedCardStyle } from '@blocksuite/affine-model'; import { @@ -71,6 +72,7 @@ export class EmbedCardCreateModal extends WithDisposable(ShadowlessElement) { } const gfx = this.host.std.get(GfxControllerIdentifier); + const crud = this.host.std.get(EdgelessCRUDIdentifier); const viewport = gfx.viewport; const surfaceModel = gfx.surface; @@ -79,7 +81,7 @@ export class EmbedCardCreateModal extends WithDisposable(ShadowlessElement) { } const center = Vec.toVec(viewport.center); - edgelessRoot.service.addBlock( + crud.addBlock( flavour, { url, diff --git a/blocksuite/blocks/src/edgeless-text-block/commands/insert-edgeless-text.ts b/blocksuite/blocks/src/edgeless-text-block/commands/insert-edgeless-text.ts index cf74080274..b7849be591 100644 --- a/blocksuite/blocks/src/edgeless-text-block/commands/insert-edgeless-text.ts +++ b/blocksuite/blocks/src/edgeless-text-block/commands/insert-edgeless-text.ts @@ -1,8 +1,9 @@ +import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface'; import { focusTextModel } from '@blocksuite/affine-components/rich-text'; import type { Command } from '@blocksuite/block-std'; +import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx'; import { Bound } from '@blocksuite/global/utils'; -import { EdgelessRootService } from '../../root-block/edgeless/edgeless-root-service.js'; import { getSurfaceBlock } from '../../surface-ref-block/utils.js'; import { EDGELESS_TEXT_BLOCK_MIN_HEIGHT, @@ -21,15 +22,16 @@ export const insertEdgelessTextCommand: Command< const { std, x, y } = ctx; const host = std.host; const doc = host.doc; - const edgelessService = std.getService('affine:page'); const surface = getSurfaceBlock(doc); - if (!(edgelessService instanceof EdgelessRootService) || !surface) { + if (!surface) { next(); return; } + const gfx = std.get(GfxControllerIdentifier); + const zoom = gfx.viewport.zoom; + const selection = gfx.selection; - const zoom = edgelessService.zoom; - const textId = edgelessService.addBlock( + const textId = std.get(EdgelessCRUDIdentifier).addBlock( 'affine:edgeless-text', { xywh: new Bound( @@ -45,13 +47,13 @@ export const insertEdgelessTextCommand: Command< const blockId = doc.addBlock('affine:paragraph', { type: 'text' }, textId); host.updateComplete .then(() => { - edgelessService.selection.set({ + selection.set({ elements: [textId], editing: true, }); - const disposable = edgelessService.selection.slots.updated.on(() => { - const editing = edgelessService.selection.editing; - const id = edgelessService.selection.selectedIds[0]; + const disposable = selection.slots.updated.on(() => { + const editing = selection.editing; + const id = selection.selectedIds[0]; if (!editing || id !== textId) { const textBlock = host.view.getBlock(textId); if (textBlock instanceof EdgelessTextBlockComponent) { diff --git a/blocksuite/blocks/src/root-block/edgeless/clipboard/clipboard.ts b/blocksuite/blocks/src/root-block/edgeless/clipboard/clipboard.ts index 61f63a0f62..ed06f9c759 100644 --- a/blocksuite/blocks/src/root-block/edgeless/clipboard/clipboard.ts +++ b/blocksuite/blocks/src/root-block/edgeless/clipboard/clipboard.ts @@ -2,6 +2,7 @@ import { addAttachments } from '@blocksuite/affine-block-attachment'; import { addImages } from '@blocksuite/affine-block-image'; import { CanvasElementType, + EdgelessCRUDIdentifier, SurfaceGroupLikeModel, TextUtils, } from '@blocksuite/affine-block-surface'; @@ -329,11 +330,7 @@ export class EdgelessClipboardController extends PageClipboard { ).serialize(); options.style = style; - const id = this.host.service.addBlock( - flavour, - options, - this.surface.model.id - ); + const id = this.crud.addBlock(flavour, options, this.surface.model.id); this.std.getOptional(TelemetryProvider)?.track('CanvasElementAdded', { control: 'canvas:paste', @@ -418,6 +415,10 @@ export class EdgelessClipboardController extends PageClipboard { return this.host.surface; } + private get crud() { + return this.std.get(EdgelessCRUDIdentifier); + } + private get toolManager() { return this.host.gfx.tool; } @@ -470,7 +471,7 @@ export class EdgelessClipboardController extends PageClipboard { if (!(await this.host.std.collection.blobSync.get(sourceId as string))) { return null; } - const attachmentId = this.host.service.addBlock( + const attachmentId = this.crud.addBlock( 'affine:attachment', { xywh, @@ -491,7 +492,7 @@ export class EdgelessClipboardController extends PageClipboard { const { xywh, style, url, caption, description, icon, image, title } = bookmark.props; - const bookmarkId = this.host.service.addBlock( + const bookmarkId = this.crud.addBlock( 'affine:bookmark', { xywh, @@ -581,10 +582,13 @@ export class EdgelessClipboardController extends PageClipboard { clipboardData.lockedBySelf = false; - const id = this.host.service.addElement( + const id = this.crud.addElement( clipboardData.type as CanvasElementType, clipboardData ); + if (!id) { + return null; + } this.std.getOptional(TelemetryProvider)?.track('CanvasElementAdded', { control: 'canvas:paste', page: 'whiteboard editor', @@ -624,7 +628,7 @@ export class EdgelessClipboardController extends PageClipboard { private _createFigmaEmbedBlock(figmaEmbed: BlockSnapshot) { const { xywh, style, url, caption, title, description } = figmaEmbed.props; - const embedFigmaId = this.host.service.addBlock( + const embedFigmaId = this.crud.addBlock( 'affine:embed-figma', { xywh, @@ -654,7 +658,7 @@ export class EdgelessClipboardController extends PageClipboard { }); } - const frameId = this.host.service.addBlock( + const frameId = this.crud.addBlock( 'affine:frame', { xywh, @@ -687,7 +691,7 @@ export class EdgelessClipboardController extends PageClipboard { assignees, } = githubEmbed.props; - const embedGithubId = this.host.service.addBlock( + const embedGithubId = this.crud.addBlock( 'affine:embed-github', { xywh, @@ -714,7 +718,7 @@ export class EdgelessClipboardController extends PageClipboard { private _createHtmlEmbedBlock(htmlEmbed: BlockSnapshot) { const { xywh, style, caption, html, design } = htmlEmbed.props; - const embedHtmlId = this.host.service.addBlock( + const embedHtmlId = this.crud.addBlock( 'affine:embed-html', { xywh, @@ -735,7 +739,7 @@ export class EdgelessClipboardController extends PageClipboard { if (!(await this.host.std.collection.blobSync.get(sourceId as string))) { return null; } - return this.host.service.addBlock( + return this.crud.addBlock( 'affine:image', { caption, @@ -760,7 +764,7 @@ export class EdgelessClipboardController extends PageClipboard { description, }); - return this.host.service.addBlock( + return this.crud.addBlock( 'affine:embed-linked-doc', { xywh, @@ -776,7 +780,7 @@ export class EdgelessClipboardController extends PageClipboard { const { xywh, style, url, caption, videoId, image, title, description } = loomEmbed.props; - const embedLoomId = this.host.service.addBlock( + const embedLoomId = this.crud.addBlock( 'affine:embed-loom', { xywh, @@ -820,7 +824,7 @@ export class EdgelessClipboardController extends PageClipboard { syncedDocEmbed.props; const referenceInfo = ReferenceInfoSchema.parse({ pageId, params }); - return this.host.service.addBlock( + return this.crud.addBlock( 'affine:embed-synced-doc', { xywh, @@ -848,7 +852,7 @@ export class EdgelessClipboardController extends PageClipboard { creatorImage, } = youtubeEmbed.props; - const embedYoutubeId = this.host.service.addBlock( + const embedYoutubeId = this.crud.addBlock( 'affine:embed-youtube', { xywh, @@ -1085,7 +1089,7 @@ export class EdgelessClipboardController extends PageClipboard { ).serialize(), }; - const noteId = edgeless.service.addBlock( + const noteId = this.crud.addBlock( 'affine:note', noteProps, this.doc.root!.id @@ -1101,7 +1105,7 @@ export class EdgelessClipboardController extends PageClipboard { if (typeof content === 'string') { TextUtils.splitIntoLines(content).forEach((line, idx) => { - edgeless.service.addBlock( + this.crud.addBlock( 'affine:paragraph', { text: new DocCollection.Y.Text(line) }, noteId, @@ -1181,7 +1185,7 @@ export class EdgelessClipboardController extends PageClipboard { sortedElements.forEach(ele => { const newIndex = idxGenerator(); - this.edgeless.service.updateElement(ele.id, { + this.crud.updateElement(ele.id, { index: newIndex, }); }); @@ -1323,6 +1327,8 @@ export class EdgelessClipboardController extends PageClipboard { getNewXYWH(data.xywh) ); + if (!element) continue; + canvasElements.push(element); allElements.push(element); diff --git a/blocksuite/blocks/src/root-block/edgeless/components/auto-complete/auto-complete-panel.ts b/blocksuite/blocks/src/root-block/edgeless/components/auto-complete/auto-complete-panel.ts index 6ae2f07a8e..f81a94e1f8 100644 --- a/blocksuite/blocks/src/root-block/edgeless/components/auto-complete/auto-complete-panel.ts +++ b/blocksuite/blocks/src/root-block/edgeless/components/auto-complete/auto-complete-panel.ts @@ -1,6 +1,7 @@ import { CanvasElementType, CommonUtils, + EdgelessCRUDIdentifier, } from '@blocksuite/affine-block-surface'; import { FontFamilyIcon, @@ -137,6 +138,10 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) { this.connector = connector; } + get crud() { + return this.std.get(EdgelessCRUDIdentifier); + } + private _addFrame() { const bound = this._generateTarget(this.connector)?.nextBound; if (!bound) return; @@ -152,7 +157,7 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) { const { service, surfaceBlockModel } = edgeless; const frameMgr = service.frame; const frameIndex = service.frames.length + 1; - const id = service.addBlock( + const id = this.crud.addBlock( 'affine:frame', { title: new DocCollection.Y.Text(`Frame ${frameIndex}`), @@ -178,7 +183,6 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) { private _addNote() { const { doc } = this.edgeless; - const service = this.edgeless.service; const target = this._getTargetXYWH( DEFAULT_NOTE_WIDTH, DEFAULT_NOTE_OVERLAY_HEIGHT @@ -186,7 +190,7 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) { if (!target) return; const { xywh, position } = target; - const id = service.addBlock( + const id = this.crud.addBlock( 'affine:note', { xywh: serializeXYWH(...xywh), @@ -205,7 +209,7 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) { id, position: position as [number, number], }; - service.updateElement(this.connector.id, { + this.crud.updateElement(this.connector.id, { target: { id, position }, }); this.edgeless.service.selection.set({ @@ -223,9 +227,10 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) { const { nextBound, position } = result; const { service } = edgeless; const id = createShapeElement(edgeless, currentSource, targetType); + if (!id) return; - service.updateElement(id, { xywh: nextBound.serialize() }); - service.updateElement(this.connector.id, { + this.crud.updateElement(id, { xywh: nextBound.serialize() }); + this.crud.updateElement(this.connector.id, { target: { id, position }, }); @@ -260,7 +265,7 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) { const textElement = edgelessService.getElementById(textId); if (!textElement) return; - edgelessService.updateElement(this.connector.id, { + this.crud.updateElement(this.connector.id, { target: { id: textId, position }, }); if (this.currentSource.group instanceof GroupElementModel) { @@ -273,7 +278,7 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) { }); this.edgeless.doc.captureSync(); } else { - const textId = edgelessService.addElement(CanvasElementType.TEXT, { + const textId = this.crud.addElement(CanvasElementType.TEXT, { xywh: bound.serialize(), text: new DocCollection.Y.Text(), textAlign: 'left', @@ -283,10 +288,11 @@ export class EdgelessAutoCompletePanel extends WithDisposable(LitElement) { fontWeight: FontWeight.Regular, fontStyle: FontStyle.Normal, }); + if (!textId) return; const textElement = edgelessService.getElementById(textId); assertInstanceOf(textElement, TextElementModel); - edgelessService.updateElement(this.connector.id, { + this.crud.updateElement(this.connector.id, { target: { id: textId, position }, }); if (this.currentSource.group instanceof GroupElementModel) { diff --git a/blocksuite/blocks/src/root-block/edgeless/components/auto-complete/edgeless-auto-complete.ts b/blocksuite/blocks/src/root-block/edgeless/components/auto-complete/edgeless-auto-complete.ts index 14acc7b094..c06c80c171 100644 --- a/blocksuite/blocks/src/root-block/edgeless/components/auto-complete/edgeless-auto-complete.ts +++ b/blocksuite/blocks/src/root-block/edgeless/components/auto-complete/edgeless-auto-complete.ts @@ -2,6 +2,7 @@ import { CanvasElementType, type ConnectionOverlay, ConnectorPathGenerator, + EdgelessCRUDIdentifier, Overlay, OverlayIdentifier, type RoughCanvas, @@ -232,12 +233,17 @@ export class EdgelessAutoComplete extends WithDisposable(LitElement) { return this.std.get(OverlayIdentifier('connection')) as ConnectionOverlay; } + get crud() { + return this.std.get(EdgelessCRUDIdentifier); + } + private _addConnector(source: Connection, target: Connection) { const { edgeless } = this; - const id = edgeless.service.addElement(CanvasElementType.CONNECTOR, { + const id = this.crud.addElement(CanvasElementType.CONNECTOR, { source, target, }); + if (!id) return null; return edgeless.service.getElementById(id) as ConnectorElementModel; } @@ -354,6 +360,7 @@ export class EdgelessAutoComplete extends WithDisposable(LitElement) { const { doc, service } = this.edgeless; const bound = this._computeNextBound(type); const id = createEdgelessElement(this.edgeless, this.current, bound); + if (!id) return; if (isShape(this.current)) { const { startPosition, endPosition } = getPosition(type); this._addConnector( diff --git a/blocksuite/blocks/src/root-block/edgeless/components/auto-complete/utils.ts b/blocksuite/blocks/src/root-block/edgeless/components/auto-complete/utils.ts index 0b15186381..ceada16dc0 100644 --- a/blocksuite/blocks/src/root-block/edgeless/components/auto-complete/utils.ts +++ b/blocksuite/blocks/src/root-block/edgeless/components/auto-complete/utils.ts @@ -281,11 +281,12 @@ export function createEdgelessElement( let element: GfxModel | null = null; if (isShape(current)) { - id = service.addElement(current.type, { + id = service.crud.addElement(current.type, { ...current.serialize(), text: new DocCollection.Y.Text(), xywh: bound.serialize(), }); + if (!id) return null; element = service.getElementById(id); } else { const { doc } = edgeless; @@ -335,11 +336,12 @@ export function createShapeElement( targetType: TARGET_SHAPE_TYPE ) { const service = edgeless.service; - const id = service.addElement('shape', { + const id = service.crud.addElement('shape', { shapeType: getShapeType(targetType), radius: getShapeRadius(targetType), text: new DocCollection.Y.Text(), }); + if (!id) return null; const element = service.getElementById(id); const group = current.group; if (group instanceof GroupElementModel && element) { diff --git a/blocksuite/blocks/src/root-block/edgeless/components/text/edgeless-connector-label-editor.ts b/blocksuite/blocks/src/root-block/edgeless/components/text/edgeless-connector-label-editor.ts index 3c5275bbf1..ddffe9c80d 100644 --- a/blocksuite/blocks/src/root-block/edgeless/components/text/edgeless-connector-label-editor.ts +++ b/blocksuite/blocks/src/root-block/edgeless/components/text/edgeless-connector-label-editor.ts @@ -1,4 +1,7 @@ -import { TextUtils } from '@blocksuite/affine-block-surface'; +import { + EdgelessCRUDIdentifier, + TextUtils, +} from '@blocksuite/affine-block-surface'; import type { RichText } from '@blocksuite/affine-components/rich-text'; import type { ConnectorElementModel } from '@blocksuite/affine-model'; import { ThemeProvider } from '@blocksuite/affine-shared/services'; @@ -60,6 +63,10 @@ export class EdgelessConnectorLabelEditor extends WithDisposable( } `; + get crud() { + return this.edgeless.std.get(EdgelessCRUDIdentifier); + } + private _isComposition = false; private _keeping = false; @@ -84,7 +91,7 @@ export class EdgelessConnectorLabelEditor extends WithDisposable( !connector.labelXYWH || labelXYWH.some((p, i) => !almostEqual(p, connector.labelXYWH![i])) ) { - edgeless.service.updateElement(connector.id, { + this.crud.updateElement(connector.id, { labelXYWH, }); } @@ -172,13 +179,13 @@ export class EdgelessConnectorLabelEditor extends WithDisposable( const len = trimed.length; if (len === 0) { // reset - edgeless.service.updateElement(connector.id, { + this.crud.updateElement(connector.id, { text: undefined, labelXYWH: undefined, labelOffset: undefined, }); } else if (len < text.length) { - edgeless.service.updateElement(connector.id, { + this.crud.updateElement(connector.id, { // @TODO: trim in Y.Text? text: new DocCollection.Y.Text(trimed), }); diff --git a/blocksuite/blocks/src/root-block/edgeless/components/text/edgeless-shape-text-editor.ts b/blocksuite/blocks/src/root-block/edgeless/components/text/edgeless-shape-text-editor.ts index e63ab3551e..dc1fbaf705 100644 --- a/blocksuite/blocks/src/root-block/edgeless/components/text/edgeless-shape-text-editor.ts +++ b/blocksuite/blocks/src/root-block/edgeless/components/text/edgeless-shape-text-editor.ts @@ -1,4 +1,8 @@ -import { CommonUtils, TextUtils } from '@blocksuite/affine-block-surface'; +import { + CommonUtils, + EdgelessCRUDIdentifier, + TextUtils, +} from '@blocksuite/affine-block-surface'; import type { RichText } from '@blocksuite/affine-components/rich-text'; import type { ShapeElementModel } from '@blocksuite/affine-model'; import { MindmapElementModel, TextResizing } from '@blocksuite/affine-model'; @@ -24,6 +28,10 @@ import { getSelectedRect } from '../../utils/query.js'; const { toRadian } = CommonUtils; export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) { + get crud() { + return this.edgeless.std.get(EdgelessCRUDIdentifier); + } + private _keeping = false; private _lastXYWH = ''; @@ -137,7 +145,7 @@ export class EdgelessShapeTextEditor extends WithDisposable(ShadowlessElement) { const [modelLeftTopX, modelLeftTopY] = this.edgeless.service.viewport.toModelCoord(leftTopX, leftTopY); - this.edgeless.service.updateElement(this.element.id, { + this.crud.updateElement(this.element.id, { xywh: new Bound( modelLeftTopX, modelLeftTopY, diff --git a/blocksuite/blocks/src/root-block/edgeless/components/text/edgeless-text-editor.ts b/blocksuite/blocks/src/root-block/edgeless/components/text/edgeless-text-editor.ts index 82612a6c4b..8e2df13dfd 100644 --- a/blocksuite/blocks/src/root-block/edgeless/components/text/edgeless-text-editor.ts +++ b/blocksuite/blocks/src/root-block/edgeless/components/text/edgeless-text-editor.ts @@ -1,4 +1,8 @@ -import { CommonUtils, TextUtils } from '@blocksuite/affine-block-surface'; +import { + CommonUtils, + EdgelessCRUDIdentifier, + TextUtils, +} from '@blocksuite/affine-block-surface'; import type { RichText } from '@blocksuite/affine-components/rich-text'; import type { TextElementModel } from '@blocksuite/affine-model'; import { ThemeProvider } from '@blocksuite/affine-shared/services'; @@ -23,6 +27,10 @@ import { getSelectedRect } from '../../utils/query.js'; const { toRadian } = CommonUtils; export class EdgelessTextEditor extends WithDisposable(ShadowlessElement) { + get crud() { + return this.edgeless.std.get(EdgelessCRUDIdentifier); + } + static BORDER_WIDTH = 1; static PADDING_HORIZONTAL = 10; @@ -137,7 +145,7 @@ export class EdgelessTextEditor extends WithDisposable(ShadowlessElement) { break; } - edgeless.service.updateElement(element.id, { + this.crud.updateElement(element.id, { xywh: bound.serialize(), }); }; diff --git a/blocksuite/blocks/src/root-block/edgeless/components/toolbar/mindmap/basket-elements.ts b/blocksuite/blocks/src/root-block/edgeless/components/toolbar/mindmap/basket-elements.ts index 775c39aa29..cf42b93f81 100644 --- a/blocksuite/blocks/src/root-block/edgeless/components/toolbar/mindmap/basket-elements.ts +++ b/blocksuite/blocks/src/root-block/edgeless/components/toolbar/mindmap/basket-elements.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { CanvasElementType } from '@blocksuite/affine-block-surface'; import { type MindmapStyle, TextElementModel } from '@blocksuite/affine-model'; import { TelemetryProvider } from '@blocksuite/affine-shared/services'; @@ -78,7 +77,7 @@ export const getMindmapRender = }); } - const mindmapId = edgelessService.addElement('mindmap', { + const mindmapId = edgelessService.crud.addElement('mindmap', { style: mindmapStyle, children: root, }) as string; @@ -113,10 +112,10 @@ export const textRender: DraggableTool['render'] = ( }); id = textId!; } else { - id = service.addElement(CanvasElementType.TEXT, { + id = service.crud.addElement(CanvasElementType.TEXT, { xywh: new Bound(bound.x, vCenter - h / 2, w, h).serialize(), text: new DocCollection.Y.Text(), - }); + }) as string; edgeless.doc.captureSync(); const textElement = edgeless.service.getElementById(id); diff --git a/blocksuite/blocks/src/root-block/edgeless/components/toolbar/mindmap/mindmap-tool-button.ts b/blocksuite/blocks/src/root-block/edgeless/components/toolbar/mindmap/mindmap-tool-button.ts index 12284ff2fb..6ffde40aff 100644 --- a/blocksuite/blocks/src/root-block/edgeless/components/toolbar/mindmap/mindmap-tool-button.ts +++ b/blocksuite/blocks/src/root-block/edgeless/components/toolbar/mindmap/mindmap-tool-button.ts @@ -1,3 +1,4 @@ +import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface'; import type { MindmapElementModel, MindmapStyle, @@ -162,6 +163,10 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin( return getMindMaps(this.theme); } + get crud() { + return this.edgeless.std.get(EdgelessCRUDIdentifier); + } + private _toggleMenu() { if (this.tryDisposePopper()) return; this.setEdgelessTool({ type: 'default' }); @@ -176,10 +181,11 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin( }, onImportMindMap: (bound: Bound) => { return importMindmap(bound).then(mindmap => { - const id = this.edgeless.service.addElement('mindmap', { + const id = this.crud.addElement('mindmap', { children: mindmap, layoutType: mindmap?.layoutType === 'left' ? 1 : 0, }); + if (!id) return; const element = this.edgeless.service.getElementById( id ) as MindmapElementModel; diff --git a/blocksuite/blocks/src/root-block/edgeless/components/toolbar/present/frame-order-menu.ts b/blocksuite/blocks/src/root-block/edgeless/components/toolbar/present/frame-order-menu.ts index 69cc7eebeb..1de5cfbe14 100644 --- a/blocksuite/blocks/src/root-block/edgeless/components/toolbar/present/frame-order-menu.ts +++ b/blocksuite/blocks/src/root-block/edgeless/components/toolbar/present/frame-order-menu.ts @@ -1,3 +1,4 @@ +import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface'; import { generateKeyBetweenV2 } from '@blocksuite/block-std/gfx'; import { DisposableGroup, @@ -98,6 +99,10 @@ export class EdgelessFrameOrderMenu extends SignalWatcher( } `; + get crud() { + return this.edgeless.std.get(EdgelessCRUDIdentifier); + } + private get _frames() { return this.edgeless.service.frames; } @@ -182,7 +187,7 @@ export class EdgelessFrameOrderMenu extends SignalWatcher( const frame = this._frames[index]; - this.edgeless.service.updateElement(frame.id, { + this.crud.updateElement(frame.id, { presentationIndex: generateKeyBetweenV2(before, after), }); this.edgeless.doc.captureSync(); diff --git a/blocksuite/blocks/src/root-block/edgeless/components/toolbar/shape/shape-draggable.ts b/blocksuite/blocks/src/root-block/edgeless/components/toolbar/shape/shape-draggable.ts index 46a86a3f64..8e57c18676 100644 --- a/blocksuite/blocks/src/root-block/edgeless/components/toolbar/shape/shape-draggable.ts +++ b/blocksuite/blocks/src/root-block/edgeless/components/toolbar/shape/shape-draggable.ts @@ -1,4 +1,7 @@ -import { CanvasElementType } from '@blocksuite/affine-block-surface'; +import { + CanvasElementType, + EdgelessCRUDIdentifier, +} from '@blocksuite/affine-block-surface'; import { ellipseSvg, roundedSvg, @@ -137,6 +140,10 @@ export class EdgelessToolbarShapeDraggable extends EdgelessToolbarToolMixin( override type = 'shape' as const; + get crud() { + return this.edgeless.std.get(EdgelessCRUDIdentifier); + } + get shapeShadow() { return this.theme === 'dark' ? '0 0 7px rgba(0, 0, 0, .22)' @@ -179,12 +186,12 @@ export class EdgelessToolbarShapeDraggable extends EdgelessToolbarToolMixin( onDrop: (el, bound) => { const xywh = bound.serialize(); const shape = el.data; - const id = this.edgeless.service.addElement(CanvasElementType.SHAPE, { + const id = this.crud.addElement(CanvasElementType.SHAPE, { shapeType: getShapeType(shape.name), xywh, radius: getShapeRadius(shape.name), }); - + if (!id) return; this.edgeless.std .getOptional(TelemetryProvider) ?.track('CanvasElementAdded', { diff --git a/blocksuite/blocks/src/root-block/edgeless/components/toolbar/shape/shape-tool-element.ts b/blocksuite/blocks/src/root-block/edgeless/components/toolbar/shape/shape-tool-element.ts index 81e577de9f..2b92787861 100644 --- a/blocksuite/blocks/src/root-block/edgeless/components/toolbar/shape/shape-tool-element.ts +++ b/blocksuite/blocks/src/root-block/edgeless/components/toolbar/shape/shape-tool-element.ts @@ -1,4 +1,7 @@ -import { CanvasElementType } from '@blocksuite/affine-block-surface'; +import { + CanvasElementType, + EdgelessCRUDIdentifier, +} from '@blocksuite/affine-block-surface'; import { getShapeRadius, getShapeType, @@ -67,6 +70,10 @@ export class EdgelessShapeToolElement extends WithDisposable(LitElement) { } `; + get crud() { + return this.edgeless.std.get(EdgelessCRUDIdentifier); + } + private readonly _addShape = (coord: Coord, padding: Coord) => { const width = 100; const height = 100; @@ -78,7 +85,7 @@ export class EdgelessShapeToolElement extends WithDisposable(LitElement) { coord.y - edgelessY - height * padding.y * zoom ); const xywh = new Bound(modelX, modelY, width, height).serialize(); - this.edgeless.service.addElement(CanvasElementType.SHAPE, { + this.crud.addElement(CanvasElementType.SHAPE, { shapeType: getShapeType(this.shape.name), xywh: xywh, radius: getShapeRadius(this.shape.name), diff --git a/blocksuite/blocks/src/root-block/edgeless/edgeless-root-service.ts b/blocksuite/blocks/src/root-block/edgeless/edgeless-root-service.ts index 32b37fff9d..42b21d9652 100644 --- a/blocksuite/blocks/src/root-block/edgeless/edgeless-root-service.ts +++ b/blocksuite/blocks/src/root-block/edgeless/edgeless-root-service.ts @@ -1,4 +1,5 @@ import { + EdgelessCRUDIdentifier, type ElementRenderer, elementRenderers, type SurfaceBlockModel, @@ -11,7 +12,6 @@ import { MindmapElementModel, RootBlockSchema, } from '@blocksuite/affine-model'; -import { EditPropsStore } from '@blocksuite/affine-shared/services'; import { clamp } from '@blocksuite/affine-shared/utils'; import type { BlockStdScope } from '@blocksuite/block-std'; import type { @@ -28,7 +28,7 @@ import { } from '@blocksuite/block-std/gfx'; import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions'; import { Bound, getCommonBound } from '@blocksuite/global/utils'; -import { type BlockModel, Slot } from '@blocksuite/store'; +import { Slot } from '@blocksuite/store'; import { effect } from '@preact/signals-core'; import { getSurfaceBlock } from '../../surface-ref-block/utils.js'; @@ -43,7 +43,6 @@ import { replaceIdMiddleware, } from './services/template-middlewares.js'; import { FIT_TO_SCREEN_PADDING } from './utils/consts.js'; -import { getLastPropsKey } from './utils/get-last-props-key.js'; import { getCursorMode } from './utils/query.js'; import { ZOOM_INITIAL, @@ -85,29 +84,6 @@ export class EdgelessRootService extends RootService implements SurfaceContext { TemplateJob = TemplateJob; - updateElement = (id: string, props: Record) => { - const element = this._surface.getElementById(id); - if (element) { - const key = getLastPropsKey( - element.type as BlockSuite.EdgelessModelKeys, - { ...element.yMap.toJSON(), ...props } - ); - key && this.std.get(EditPropsStore).recordLastProps(key, props); - this._surface.updateElement(id, props); - return; - } - - const block = this.doc.getBlockById(id); - if (block) { - const key = getLastPropsKey( - block.flavour as BlockSuite.EdgelessModelKeys, - { ...block.yBlock.toJSON(), ...props } - ); - key && this.std.get(EditPropsStore).recordLastProps(key, props); - this.doc.updateBlock(block, props); - } - }; - get blocks(): GfxBlockModel[] { return this.layer.blocks; } @@ -179,6 +155,10 @@ export class EdgelessRootService extends RootService implements SurfaceContext { return this.viewport.zoom; } + get crud() { + return this.std.get(EdgelessCRUDIdentifier); + } + constructor(std: BlockStdScope, flavourProvider: { flavour: string }) { super(std, flavourProvider); const surface = getSurfaceBlock(this.doc); @@ -216,44 +196,11 @@ export class EdgelessRootService extends RootService implements SurfaceContext { ); } - addBlock( - flavour: string, - props: Record, - parent?: string | BlockModel, - parentIndex?: number - ) { - const key = getLastPropsKey(flavour as BlockSuite.EdgelessModelKeys, props); - if (key) { - props = this.std.get(EditPropsStore).applyLastProps(key, props); - } - - const nProps = { - ...props, - index: this.generateIndex(), - }; - return this.doc.addBlock(flavour as never, nProps, parent, parentIndex); - } - - addElement>(type: string, props: T) { - const key = getLastPropsKey(type as BlockSuite.EdgelessModelKeys, props); - if (key) { - props = this.std.get(EditPropsStore).applyLastProps(key, props) as T; - } - - const nProps = { - ...props, - type, - index: props.index ?? this.generateIndex(), - }; - const id = this._surface.addElement(nProps); - return id; - } - createGroup(elements: BlockSuite.EdgelessModel[] | string[]) { const groups = this.elements.filter( el => el.type === 'group' ) as GroupElementModel[]; - const groupId = this.addElement('group', { + const groupId = this.crud.addElement('group', { children: elements.reduce( (pre, el) => { const id = typeof el === 'string' ? el : el.id; @@ -290,12 +237,15 @@ export class EdgelessRootService extends RootService implements SurfaceContext { if (parent !== null) { selection.selectedElements.forEach(element => { - // eslint-disable-next-line unicorn/prefer-dom-node-remove + // oxlint-disable-next-line unicorn/prefer-dom-node-remove parent.removeChild(element); }); } const groupId = this.createGroup(selection.selectedElements); + if (!groupId) { + return; + } const group = this.surface.getElementById(groupId); if (parent !== null && group) { @@ -487,12 +437,12 @@ export class EdgelessRootService extends RootService implements SurfaceContext { } if (parent !== null) { - // eslint-disable-next-line unicorn/prefer-dom-node-remove + // oxlint-disable-next-line unicorn/prefer-dom-node-remove parent.removeChild(group); } elements.forEach(element => { - // eslint-disable-next-line unicorn/prefer-dom-node-remove + // oxlint-disable-next-line unicorn/prefer-dom-node-remove group.removeChild(element); }); diff --git a/blocksuite/blocks/src/root-block/edgeless/gfx-tool/eraser-tool.ts b/blocksuite/blocks/src/root-block/edgeless/gfx-tool/eraser-tool.ts index c752c7f465..c730c6bde6 100644 --- a/blocksuite/blocks/src/root-block/edgeless/gfx-tool/eraser-tool.ts +++ b/blocksuite/blocks/src/root-block/edgeless/gfx-tool/eraser-tool.ts @@ -1,5 +1,6 @@ import { CommonUtils, + EdgelessCRUDIdentifier, Overlay, type SurfaceBlockComponent, } from '@blocksuite/affine-block-surface'; @@ -7,7 +8,6 @@ import type { PointerEventState } from '@blocksuite/block-std'; import { BaseTool } from '@blocksuite/block-std/gfx'; import { Bound, type IVec } from '@blocksuite/global/utils'; -import { deleteElementsV2 } from '../utils/crud.js'; import { isTopLevelBlock } from '../utils/query.js'; const { getSvgPathFromStroke, getStroke, linePolygonIntersects } = CommonUtils; @@ -99,7 +99,9 @@ export class EraserTool extends BaseTool { } override dragEnd(_: PointerEventState): void { - deleteElementsV2(this.gfx, Array.from(this._eraseTargets)); + this.gfx.std + .get(EdgelessCRUDIdentifier) + .deleteElements(Array.from(this._eraseTargets)); this._reset(); this.doc.captureSync(); } diff --git a/blocksuite/blocks/src/root-block/edgeless/middlewares/base.ts b/blocksuite/blocks/src/root-block/edgeless/middlewares/base.ts index 58a63de12c..38c055b8ab 100644 --- a/blocksuite/blocks/src/root-block/edgeless/middlewares/base.ts +++ b/blocksuite/blocks/src/root-block/edgeless/middlewares/base.ts @@ -1,11 +1,10 @@ +import { getLastPropsKey } from '@blocksuite/affine-block-surface'; import { EditPropsStore } from '@blocksuite/affine-shared/services'; import { type SurfaceMiddleware, SurfaceMiddlewareBuilder, } from '@blocksuite/block-std/gfx'; -import { getLastPropsKey } from '../utils/get-last-props-key.js'; - export class EditPropsMiddlewareBuilder extends SurfaceMiddlewareBuilder { static override key = 'editProps'; diff --git a/blocksuite/blocks/src/root-block/edgeless/utils/common.ts b/blocksuite/blocks/src/root-block/edgeless/utils/common.ts index bb105c4c8b..99f4c68588 100644 --- a/blocksuite/blocks/src/root-block/edgeless/utils/common.ts +++ b/blocksuite/blocks/src/root-block/edgeless/utils/common.ts @@ -1,3 +1,4 @@ +import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface'; import { focusTextModel } from '@blocksuite/affine-components/rich-text'; import { DEFAULT_NOTE_HEIGHT, @@ -18,7 +19,6 @@ import { } from '@blocksuite/global/utils'; import { DEFAULT_NOTE_OFFSET_X, DEFAULT_NOTE_OFFSET_Y } from './consts.js'; -import { addBlock } from './crud.js'; export function addNoteAtPoint( std: BlockStdScope, @@ -37,6 +37,7 @@ export function addNoteAtPoint( } = {} ) { const gfx = std.get(GfxControllerIdentifier); + const crud = std.get(EdgelessCRUDIdentifier); const { width = DEFAULT_NOTE_WIDTH, height = DEFAULT_NOTE_HEIGHT, @@ -47,8 +48,7 @@ export function addNoteAtPoint( scale = 1, } = options; const [x, y] = gfx.viewport.toModelCoord(point.x, point.y); - const blockId = addBlock( - std, + const blockId = crud.addBlock( 'affine:note', { xywh: serializeXYWH( @@ -63,7 +63,7 @@ export function addNoteAtPoint( noteIndex ); - gfx.std.getOptional(TelemetryProvider)?.track('CanvasElementAdded', { + std.getOptional(TelemetryProvider)?.track('CanvasElementAdded', { control: 'canvas:draw', page: 'whiteboard editor', module: 'toolbar', diff --git a/blocksuite/blocks/src/root-block/edgeless/utils/connector.ts b/blocksuite/blocks/src/root-block/edgeless/utils/connector.ts index ec504ab43e..a331493265 100644 --- a/blocksuite/blocks/src/root-block/edgeless/utils/connector.ts +++ b/blocksuite/blocks/src/root-block/edgeless/utils/connector.ts @@ -1,3 +1,5 @@ +import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface'; + import type { EdgelessRootService } from '../edgeless-root-service.js'; /** @@ -12,14 +14,15 @@ export function moveConnectors( service: EdgelessRootService ) { const connectors = service.surface.getConnectors(originId); + const crud = service.std.get(EdgelessCRUDIdentifier); connectors.forEach(connector => { if (connector.source.id === originId) { - service.updateElement(connector.id, { + crud.updateElement(connector.id, { source: { ...connector.source, id: targetId }, }); } if (connector.target.id === originId) { - service.updateElement(connector.id, { + crud.updateElement(connector.id, { target: { ...connector.target, id: targetId }, }); } diff --git a/blocksuite/blocks/src/root-block/edgeless/utils/crud.ts b/blocksuite/blocks/src/root-block/edgeless/utils/crud.ts index a63bd9c960..56c11430b2 100644 --- a/blocksuite/blocks/src/root-block/edgeless/utils/crud.ts +++ b/blocksuite/blocks/src/root-block/edgeless/utils/crud.ts @@ -1,15 +1,5 @@ -import type { SurfaceBlockModel } from '@blocksuite/affine-block-surface'; -import { EditPropsStore } from '@blocksuite/affine-shared/services'; -import type { BlockStdScope } from '@blocksuite/block-std'; -import { - type GfxController, - GfxControllerIdentifier, -} from '@blocksuite/block-std/gfx'; -import type { BlockModel } from '@blocksuite/store'; - import type { Connectable } from '../../../_common/utils/index.js'; import type { EdgelessRootBlockComponent } from '../index.js'; -import { getLastPropsKey } from './get-last-props-key.js'; import { isConnectable, isNoteBlock } from './query.js'; /** @@ -42,51 +32,3 @@ export function deleteElements( } }); } - -export function deleteElementsV2( - gfx: GfxController, - elements: BlockSuite.EdgelessModel[] -) { - const set = new Set(elements); - - elements.forEach(element => { - if (isConnectable(element)) { - const connectors = (gfx.surface as SurfaceBlockModel).getConnectors( - element.id - ); - connectors.forEach(connector => set.add(connector)); - } - }); - - set.forEach(element => { - if (isNoteBlock(element)) { - const children = gfx.doc.root?.children ?? []; - if (children.length > 1) { - gfx.doc.deleteBlock(element); - } - } else { - gfx.deleteElement(element.id); - } - }); -} - -export function addBlock( - std: BlockStdScope, - flavour: BlockSuite.EdgelessModelKeys, - props: Record, - parentId?: string | BlockModel, - parentIndex?: number -) { - const gfx = std.get(GfxControllerIdentifier); - const key = getLastPropsKey(flavour as BlockSuite.EdgelessModelKeys, props); - if (key) { - props = std.get(EditPropsStore).applyLastProps(key, props); - } - - const nProps = { - ...props, - index: gfx.layer.generateIndex(), - }; - - return std.doc.addBlock(flavour as never, nProps, parentId, parentIndex); -} diff --git a/blocksuite/blocks/src/root-block/edgeless/utils/text.ts b/blocksuite/blocks/src/root-block/edgeless/utils/text.ts index b9219b7569..c8c1a9e63c 100644 --- a/blocksuite/blocks/src/root-block/edgeless/utils/text.ts +++ b/blocksuite/blocks/src/root-block/edgeless/utils/text.ts @@ -1,5 +1,6 @@ import { CanvasElementType, + EdgelessCRUDIdentifier, type IModelCoord, TextUtils, } from '@blocksuite/affine-block-surface'; @@ -12,11 +13,7 @@ import { ShapeElementModel, TextElementModel } from '@blocksuite/affine-model'; import type { PointerEventState } from '@blocksuite/block-std'; import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions'; import type { IVec } from '@blocksuite/global/utils'; -import { - assertExists, - assertInstanceOf, - Bound, -} from '@blocksuite/global/utils'; +import { assertInstanceOf, Bound } from '@blocksuite/global/utils'; import { DocCollection } from '@blocksuite/store'; import { EdgelessConnectorLabelEditor } from '../components/text/edgeless-connector-label-editor.js'; @@ -77,7 +74,9 @@ export function mountShapeTextEditor( if (!shapeElement.text) { const text = new DocCollection.Y.Text(); - edgeless.service.updateElement(shapeElement.id, { text }); + edgeless.std + .get(EdgelessCRUDIdentifier) + .updateElement(shapeElement.id, { text }); } const updatedElement = edgeless.service.getElementById(shapeElement.id); @@ -164,13 +163,16 @@ export function addText( event.x, event.y ); - const id = edgeless.service.addElement(CanvasElementType.TEXT, { - xywh: new Bound(modelX, modelY, 32, 32).serialize(), - text: new DocCollection.Y.Text(), - }); + const id = edgeless.std + .get(EdgelessCRUDIdentifier) + .addElement(CanvasElementType.TEXT, { + xywh: new Bound(modelX, modelY, 32, 32).serialize(), + text: new DocCollection.Y.Text(), + }); + if (!id) return; edgeless.doc.captureSync(); const textElement = edgeless.service.getElementById(id); - assertExists(textElement); + if (!textElement) return; if (textElement instanceof TextElementModel) { mountTextElementEditor(textElement, edgeless); } @@ -203,7 +205,7 @@ export function mountConnectorLabelEditor( labelXYWH = bounds.toXYWH(); } - edgeless.service.updateElement(connector.id, { + edgeless.std.get(EdgelessCRUDIdentifier).updateElement(connector.id, { text, labelXYWH, labelOffset: { ...labelOffset }, diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/align-button.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/align-button.ts index 6c71e4dd8e..674a68f775 100644 --- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/align-button.ts +++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/align-button.ts @@ -1,4 +1,7 @@ -import { updateXYWH } from '@blocksuite/affine-block-surface'; +import { + EdgelessCRUDIdentifier, + updateXYWH, +} from '@blocksuite/affine-block-surface'; import { AlignBottomIcon, AlignDistributeHorizontallyIcon, @@ -267,7 +270,7 @@ export class EdgelessAlignButton extends WithDisposable(LitElement) { } private _updateXYWH(ele: BlockSuite.EdgelessModel, bound: Bound) { - const { updateElement } = this.edgeless.service; + const { updateElement } = this.edgeless.std.get(EdgelessCRUDIdentifier); const { updateBlock } = this.edgeless.doc; updateXYWH(ele, bound, updateElement, updateBlock); } diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-brush-button.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-brush-button.ts index c4db36174e..74c3cf4870 100644 --- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-brush-button.ts +++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-brush-button.ts @@ -1,3 +1,4 @@ +import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface'; import type { BrushElementModel, BrushProps, @@ -57,10 +58,7 @@ export class EdgelessChangeBrushButton extends WithDisposable(LitElement) { pickColor = (event: PickColorEvent) => { if (event.type === 'pick') { this.elements.forEach(ele => - this.service.updateElement( - ele.id, - packColor('color', { ...event.detail }) - ) + this.crud.updateElement(ele.id, packColor('color', { ...event.detail })) ); return; } @@ -93,6 +91,10 @@ export class EdgelessChangeBrushButton extends WithDisposable(LitElement) { return this.edgeless.surface; } + get crud() { + return this.edgeless.std.get(EdgelessCRUDIdentifier); + } + private _setBrushProp( key: K, value: BrushProps[K] @@ -101,7 +103,7 @@ export class EdgelessChangeBrushButton extends WithDisposable(LitElement) { this.elements .filter(notEqual(key, value)) .forEach(element => - this.service.updateElement(element.id, { [key]: value }) + this.crud.updateElement(element.id, { [key]: value }) ); } diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-connector-button.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-connector-button.ts index b1786de2fa..561f114931 100644 --- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-connector-button.ts +++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-connector-button.ts @@ -1,3 +1,4 @@ +import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface'; import { AddTextIcon, ConnectorCWithArrowIcon, @@ -222,10 +223,14 @@ const MODE_CHOOSE: [ConnectorMode, () => TemplateResult<1>][] = [ ] as const; export class EdgelessChangeConnectorButton extends WithDisposable(LitElement) { + get crud() { + return this.edgeless.std.get(EdgelessCRUDIdentifier); + } + pickColor = (event: PickColorEvent) => { if (event.type === 'pick') { this.elements.forEach(ele => - this.service.updateElement( + this.crud.updateElement( ele.id, packColor('stroke', { ...event.detail }) ) @@ -257,7 +262,7 @@ export class EdgelessChangeConnectorButton extends WithDisposable(LitElement) { if (frontEndpointStyle === rearEndpointStyle) return; this.elements.forEach(element => - this.service.updateElement(element.id, { + this.crud.updateElement(element.id, { frontEndpointStyle: rearEndpointStyle, rearEndpointStyle: frontEndpointStyle, }) @@ -286,7 +291,7 @@ export class EdgelessChangeConnectorButton extends WithDisposable(LitElement) { : 'rearEndpointStyle']: style, }; this.elements.forEach(element => - this.service.updateElement(element.id, { ...props }) + this.crud.updateElement(element.id, { ...props }) ); } @@ -297,7 +302,7 @@ export class EdgelessChangeConnectorButton extends WithDisposable(LitElement) { this.elements .filter(notEqual(key, value)) .forEach(element => - this.service.updateElement(element.id, { [key]: value }) + this.crud.updateElement(element.id, { [key]: value }) ); } diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-embed-card-button.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-embed-card-button.ts index 51b5eb2536..2bb506e71d 100644 --- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-embed-card-button.ts +++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-embed-card-button.ts @@ -2,6 +2,7 @@ import { getDocContentWithMaxLength, getEmbedCardIcons, } from '@blocksuite/affine-block-embed'; +import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface'; import { CaptionIcon, CenterPeekIcon, @@ -114,6 +115,10 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) { } `; + get crud() { + return this.edgeless.std.get(EdgelessCRUDIdentifier); + } + private readonly _convertToCardView = () => { if (this._isCardView) { return; @@ -146,7 +151,7 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) { bound.w = EMBED_CARD_WIDTH[targetStyle]; bound.h = EMBED_CARD_HEIGHT[targetStyle]; - const newId = this.edgeless.service.addBlock( + const newId = this.crud.addBlock( targetFlavour, { url, xywh: bound.serialize(), style: targetStyle, caption }, this.edgeless.surface.model @@ -197,7 +202,7 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) { bound.w = EMBED_CARD_WIDTH[targetStyle]; bound.h = EMBED_CARD_HEIGHT[targetStyle]; - const newId = this.edgeless.service.addBlock( + const newId = this.crud.addBlock( flavour, { url, @@ -206,6 +211,7 @@ export class EdgelessChangeEmbedCardButton extends WithDisposable(LitElement) { }, this.edgeless.surface.model ); + if (!newId) return; this.std.command.exec('reassociateConnectors', { oldId: id, diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-frame-button.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-frame-button.ts index b031ec2e82..14444eebb4 100644 --- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-frame-button.ts +++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-frame-button.ts @@ -1,3 +1,4 @@ +import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface'; import { NoteIcon, RenameIcon, @@ -51,10 +52,14 @@ function getMostCommonColor( } export class EdgelessChangeFrameButton extends WithDisposable(LitElement) { + get crud() { + return this.edgeless.std.get(EdgelessCRUDIdentifier); + } + pickColor = (event: PickColorEvent) => { if (event.type === 'pick') { this.frames.forEach(ele => - this.service.updateElement( + this.crud.updateElement( ele.id, packColor('background', { ...event.detail }) ) @@ -116,7 +121,7 @@ export class EdgelessChangeFrameButton extends WithDisposable(LitElement) { private _setFrameBackground(color: string) { this.frames.forEach(frame => { - this.service.updateElement(frame.id, { background: color }); + this.crud.updateElement(frame.id, { background: color }); }); } diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-note-button.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-note-button.ts index 23aa1e8b64..ae609dec6b 100644 --- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-note-button.ts +++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-note-button.ts @@ -1,3 +1,4 @@ +import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface'; import { ExpandIcon, LineStyleIcon, @@ -78,6 +79,10 @@ function getMostCommonBackground( } export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { + get crud() { + return this.edgeless.std.get(EdgelessCRUDIdentifier); + } + private readonly _setBorderRadius = (borderRadius: number) => { this.notes.forEach(note => { const props = { @@ -88,7 +93,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { }, }, }; - this.edgeless.service.updateElement(note.id, props); + this.crud.updateElement(note.id, props); }); }; @@ -111,7 +116,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { if (event.type === 'pick') { this.notes.forEach(element => { const props = packColor('background', { ...event.detail }); - this.edgeless.service.updateElement(element.id, props); + this.crud.updateElement(element.id, props); }); return; } @@ -142,7 +147,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { private _setBackground(background: string) { this.notes.forEach(element => { - this.edgeless.service.updateElement(element.id, { background }); + this.crud.updateElement(element.id, { background }); }); } @@ -173,7 +178,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { return; } - this.edgeless.service.updateElement(note.id, { displayMode: newMode }); + this.crud.updateElement(note.id, { displayMode: newMode }); const noteParent = this.doc.getParent(note); assertExists(noteParent); @@ -208,7 +213,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { }, }, }; - this.edgeless.service.updateElement(note.id, props); + this.crud.updateElement(note.id, props); }); } @@ -222,7 +227,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { }, }, }; - this.edgeless.service.updateElement(note.id, props); + this.crud.updateElement(note.id, props); }); } @@ -236,7 +241,7 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { }, }, }; - this.edgeless.service.updateElement(note.id, props); + this.crud.updateElement(note.id, props); }); } diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-shape-button.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-shape-button.ts index fb9ffa13fe..810c17157f 100644 --- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-shape-button.ts +++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-shape-button.ts @@ -1,3 +1,4 @@ +import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface'; import { AddTextIcon, ChangeShapeIcon, @@ -155,6 +156,10 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) { return this.edgeless.service; } + get crud() { + return this.edgeless.std.get(EdgelessCRUDIdentifier); + } + #pickColor>( key: K ) { @@ -166,7 +171,7 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) { if (key === 'fillColor' && !ele.filled) { Object.assign(props, { filled: true }); } - this.service.updateElement(ele.id, props); + this.crud.updateElement(ele.id, props); }); return; } @@ -199,25 +204,25 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) { const filled = !isTransparent(fillColor); const color = this._getTextColor(fillColor); this.elements.forEach(ele => - this.service.updateElement(ele.id, { filled, fillColor, color }) + this.crud.updateElement(ele.id, { filled, fillColor, color }) ); } private _setShapeStrokeColor(strokeColor: string) { this.elements.forEach(ele => - this.service.updateElement(ele.id, { strokeColor }) + this.crud.updateElement(ele.id, { strokeColor }) ); } private _setShapeStrokeStyle(strokeStyle: StrokeStyle) { this.elements.forEach(ele => - this.service.updateElement(ele.id, { strokeStyle }) + this.crud.updateElement(ele.id, { strokeStyle }) ); } private _setShapeStrokeWidth(strokeWidth: number) { this.elements.forEach(ele => - this.service.updateElement(ele.id, { strokeWidth }) + this.crud.updateElement(ele.id, { strokeWidth }) ); } @@ -226,7 +231,7 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) { shapeStyle === ShapeStyle.General ? FontFamily.Inter : FontFamily.Kalam; this.elements.forEach(ele => { - this.service.updateElement(ele.id, { shapeStyle, fontFamily }); + this.crud.updateElement(ele.id, { shapeStyle, fontFamily }); }); } @@ -257,7 +262,7 @@ export class EdgelessChangeShapeButton extends WithDisposable(LitElement) { this._shapePanel.slots.select.on(shapeName => { this.edgeless.doc.captureSync(); this.elements.forEach(element => { - this.service.updateElement(element.id, { + this.crud.updateElement(element.id, { shapeType: getShapeType(shapeName), radius: getShapeRadius(shapeName), }); diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-text-menu.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-text-menu.ts index e207b5b35e..d22300787c 100644 --- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-text-menu.ts +++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/change-text-menu.ts @@ -1,6 +1,6 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { ConnectorUtils, + EdgelessCRUDIdentifier, normalizeShapeBound, TextUtils, } from '@blocksuite/affine-block-surface'; @@ -176,6 +176,10 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) { } `; + get crud() { + return this.edgeless.std.get(EdgelessCRUDIdentifier); + } + private readonly _setFontFamily = (fontFamily: FontFamily) => { const currentFontWeight = getMostCommonFontWeight(this.elements); const fontWeight = TextUtils.isFontWeightSupported( @@ -194,7 +198,7 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) { const props = { fontFamily, fontWeight, fontStyle }; this.elements.forEach(element => { - this.service.updateElement(element.id, buildProps(element, props)); + this.crud.updateElement(element.id, buildProps(element, props)); this._updateElementBound(element); }); }; @@ -202,7 +206,7 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) { private readonly _setFontSize = (fontSize: number) => { const props = { fontSize }; this.elements.forEach(element => { - this.service.updateElement(element.id, buildProps(element, props)); + this.crud.updateElement(element.id, buildProps(element, props)); this._updateElementBound(element); }); }; @@ -213,7 +217,7 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) { ) => { const props = { fontWeight, fontStyle }; this.elements.forEach(element => { - this.service.updateElement(element.id, buildProps(element, props)); + this.crud.updateElement(element.id, buildProps(element, props)); this._updateElementBound(element); }); }; @@ -221,14 +225,14 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) { private readonly _setTextAlign = (textAlign: TextAlign) => { const props = { textAlign }; this.elements.forEach(element => { - this.service.updateElement(element.id, buildProps(element, props)); + this.crud.updateElement(element.id, buildProps(element, props)); }); }; private readonly _setTextColor = ({ detail: color }: ColorEvent) => { const props = { color }; this.elements.forEach(element => { - this.service.updateElement(element.id, buildProps(element, props)); + this.crud.updateElement(element.id, buildProps(element, props)); }); }; @@ -257,7 +261,7 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) { }, Bound.fromXYWH(element.deserializedXYWH) ); - this.service.updateElement(element.id, { + this.crud.updateElement(element.id, { xywh: newBound.serialize(), }); } else if ( @@ -285,7 +289,7 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) { prevBounds ); bounds.center = center; - this.service.updateElement(element.id, { + this.crud.updateElement(element.id, { labelXYWH: bounds.toXYWH(), }); } else if ( @@ -296,7 +300,7 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) { element, Bound.fromXYWH(element.deserializedXYWH) ); - this.service.updateElement(element.id, { + this.crud.updateElement(element.id, { xywh: newBound.serialize(), }); } @@ -307,7 +311,7 @@ export class EdgelessChangeTextMenu extends WithDisposable(LitElement) { if (event.type === 'pick') { this.elements.forEach(element => { const props = packColor('color', { ...event.detail }); - this.service.updateElement(element.id, buildProps(element, props)); + this.crud.updateElement(element.id, buildProps(element, props)); this._updateElementBound(element); }); return; diff --git a/blocksuite/blocks/src/root-block/widgets/element-toolbar/more-menu/config.ts b/blocksuite/blocks/src/root-block/widgets/element-toolbar/more-menu/config.ts index 0d9f99c618..d88796814b 100644 --- a/blocksuite/blocks/src/root-block/widgets/element-toolbar/more-menu/config.ts +++ b/blocksuite/blocks/src/root-block/widgets/element-toolbar/more-menu/config.ts @@ -9,6 +9,7 @@ import { promptDocTitle, } from '@blocksuite/affine-block-embed'; import type { ImageBlockComponent } from '@blocksuite/affine-block-image'; +import { EdgelessCRUDIdentifier } from '@blocksuite/affine-block-surface'; import { isPeekable, peek } from '@blocksuite/affine-components/peek'; import type { MenuItemGroup } from '@blocksuite/affine-components/toolbar'; import { TelemetryProvider } from '@blocksuite/affine-shared/services'; @@ -253,8 +254,9 @@ export const conversionsGroup: MenuItemGroup = { if (title === null) return; const linkedDoc = createLinkedDocFromNote(doc, element, title); + const crud = std.get(EdgelessCRUDIdentifier); // insert linked doc card - const cardId = service.addBlock( + const cardId = crud.addBlock( 'affine:embed-synced-doc', { xywh: element.xywh, @@ -300,15 +302,7 @@ export const conversionsGroup: MenuItemGroup = { icon: LinkedPageIcon({ width: '20', height: '20' }), label: 'Create linked doc', type: 'create-linked-doc', - action: async ({ - doc, - selection, - service, - surface, - edgeless, - host, - std, - }) => { + action: async ({ doc, selection, surface, edgeless, host, std }) => { const title = await promptDocTitle(std); if (title === null) return; @@ -318,6 +312,7 @@ export const conversionsGroup: MenuItemGroup = { elements, title ); + const crud = std.get(EdgelessCRUDIdentifier); // delete selected elements doc.transact(() => { deleteElements(edgeless, elements); @@ -326,7 +321,7 @@ export const conversionsGroup: MenuItemGroup = { const width = 364; const height = 390; const bound = getCommonBoundWithRotation(elements); - const cardId = service.addBlock( + const cardId = crud.addBlock( 'affine:embed-linked-doc', { xywh: `[${bound.center[0] - width / 2}, ${bound.center[1] - height / 2}, ${width}, ${height}]`, diff --git a/blocksuite/presets/src/__tests__/edgeless/group.spec.ts b/blocksuite/presets/src/__tests__/edgeless/group.spec.ts index 178850141a..fbb47f9766 100644 --- a/blocksuite/presets/src/__tests__/edgeless/group.spec.ts +++ b/blocksuite/presets/src/__tests__/edgeless/group.spec.ts @@ -5,6 +5,7 @@ import { LayoutType, NoteDisplayMode, } from '@blocksuite/blocks'; +import { assertExists } from '@blocksuite/global/utils'; import { DocCollection } from '@blocksuite/store'; import { beforeEach, describe, expect, test } from 'vitest'; @@ -26,9 +27,9 @@ describe('group', () => { const map = new DocCollection.Y.Map(); const ids = Array.from({ length: 2 }) .map(() => { - const id = service.addElement('shape', { + const id = service.crud.addElement('shape', { shapeType: 'rect', - }); + })!; map.set(id, true); return id; @@ -40,7 +41,7 @@ describe('group', () => { return id; }) ); - service.addElement('group', { children: map }); + service.crud.addElement('group', { children: map }); doc.captureSync(); expect(service.elements.length).toBe(3); @@ -64,14 +65,15 @@ describe('group', () => { const map = new DocCollection.Y.Map(); const doc = service.doc; const noteId = addNote(doc); - const shapeId = service.addElement('shape', { + const shapeId = service.crud.addElement('shape', { shapeType: 'rect', }); + assertExists(shapeId); map.set(noteId, true); map.set(shapeId, true); - const groupId = service.addElement('group', { children: map }); - + const groupId = service.crud.addElement('group', { children: map }); + assertExists(groupId); expect(service.elements.length).toBe(2); expect(doc.getBlock(noteId)).toBeDefined(); doc.captureSync(); @@ -86,14 +88,16 @@ describe('group', () => { }); test("group's xywh should update automatically when children change", async () => { - const shape1 = service.addElement('shape', { + const shape1 = service.crud.addElement('shape', { shapeType: 'rect', xywh: '[0,0,100,100]', }); - const shape2 = service.addElement('shape', { + assertExists(shape1); + const shape2 = service.crud.addElement('shape', { shapeType: 'rect', xywh: '[100,100,100,100]', }); + assertExists(shape2); const note1 = addNote(doc, { displayMode: NoteDisplayMode.DocAndEdgeless, xywh: '[200,200,800,100]', @@ -114,7 +118,9 @@ describe('group', () => { children.set(shape2, true); children.set(note1, true); - const groupId = service.addElement('group', { children }); + const groupId = service.crud.addElement('group', { children }); + assertExists(groupId); + const group = service.getElementById(groupId) as GroupElementModel; const assertInitial = () => { expect(group.x).toBe(0); @@ -139,7 +145,7 @@ describe('group', () => { await wait(); assertInitial(); - service.updateElement(note1, { + service.crud.updateElement(note1, { xywh: '[300,300,800,100]', }); await wait(); @@ -165,7 +171,7 @@ describe('group', () => { await wait(); assertInitial(); - service.updateElement(shape1, { + service.crud.updateElement(shape1, { xywh: '[100,100,100,100]', }); await wait(); @@ -182,7 +188,8 @@ describe('group', () => { test('empty group should have all zero xywh', () => { const map = new DocCollection.Y.Map(); - const groupId = service.addElement('group', { children: map }); + const groupId = service.crud.addElement('group', { children: map }); + assertExists(groupId); const group = service.getElementById(groupId) as GroupElementModel; expect(group.x).toBe(0); @@ -193,9 +200,9 @@ describe('group', () => { test('descendant of group should not contain itself', () => { const groupIds = [1, 2, 3].map(_ => { - return service.addElement('group', { + return service.crud.addElement('group', { children: new DocCollection.Y.Map(), - }); + }) as string; }); const groups = groupIds.map( id => service.getElementById(id) as GroupElementModel @@ -245,7 +252,8 @@ describe('mindmap', () => { }, ], }; - const mindmapId = service.addElement('mindmap', { children: tree }); + const mindmapId = service.crud.addElement('mindmap', { children: tree }); + assertExists(mindmapId); const mindmap = () => service.getElementById(mindmapId) as MindmapElementModel; @@ -292,10 +300,11 @@ describe('mindmap', () => { }, ], }; - const mindmapId = service.addElement('mindmap', { + const mindmapId = service.crud.addElement('mindmap', { type: LayoutType.RIGHT, children: tree, }); + assertExists(mindmapId); const mindmap = () => service.getElementById(mindmapId) as MindmapElementModel; @@ -336,10 +345,11 @@ describe('mindmap', () => { }, ], }; - const mindmapId = service.addElement('mindmap', { + const mindmapId = service.crud.addElement('mindmap', { type: LayoutType.RIGHT, children: tree, }); + assertExists(mindmapId); const mindmap = () => service.getElementById(mindmapId) as MindmapElementModel; diff --git a/blocksuite/presets/src/__tests__/edgeless/last-props.spec.ts b/blocksuite/presets/src/__tests__/edgeless/last-props.spec.ts index cabf771adc..ae62d6884e 100644 --- a/blocksuite/presets/src/__tests__/edgeless/last-props.spec.ts +++ b/blocksuite/presets/src/__tests__/edgeless/last-props.spec.ts @@ -24,6 +24,7 @@ import { ShapeType, type TextElementModel, } from '@blocksuite/blocks'; +import { assertExists } from '@blocksuite/global/utils'; import { beforeEach, describe, expect, test } from 'vitest'; import { getDocRootBlock } from '../utils/edgeless.js'; @@ -45,10 +46,13 @@ describe('apply last props', () => { test('shapes', () => { // rect shape - const rectId = service.addElement('shape', { shapeType: ShapeType.Rect }); + const rectId = service.crud.addElement('shape', { + shapeType: ShapeType.Rect, + }); + assertExists(rectId); const rectShape = service.getElementById(rectId) as ShapeElementModel; expect(rectShape.fillColor).toBe(ShapeFillColor.Yellow); - service.updateElement(rectId, { + service.crud.updateElement(rectId, { fillColor: ShapeFillColor.Orange, }); expect( @@ -57,12 +61,13 @@ describe('apply last props', () => { ).toBe(ShapeFillColor.Orange); // diamond shape - const diamondId = service.addElement('shape', { + const diamondId = service.crud.addElement('shape', { shapeType: ShapeType.Diamond, }); + assertExists(diamondId); const diamondShape = service.getElementById(diamondId) as ShapeElementModel; expect(diamondShape.fillColor).toBe(ShapeFillColor.Yellow); - service.updateElement(diamondId, { + service.crud.updateElement(diamondId, { fillColor: ShapeFillColor.Blue, }); expect( @@ -71,15 +76,16 @@ describe('apply last props', () => { ).toBe(ShapeFillColor.Blue); // rounded rect shape - const roundedRectId = service.addElement('shape', { + const roundedRectId = service.crud.addElement('shape', { shapeType: ShapeType.Rect, radius: 0.1, }); + assertExists(roundedRectId); const roundedRectShape = service.getElementById( roundedRectId ) as ShapeElementModel; expect(roundedRectShape.fillColor).toBe(ShapeFillColor.Yellow); - service.updateElement(roundedRectId, { + service.crud.updateElement(roundedRectId, { fillColor: ShapeFillColor.Green, }); expect( @@ -87,22 +93,27 @@ describe('apply last props', () => { ).toBe(ShapeFillColor.Green); // apply last props - const rectId2 = service.addElement('shape', { shapeType: ShapeType.Rect }); + const rectId2 = service.crud.addElement('shape', { + shapeType: ShapeType.Rect, + }); + assertExists(rectId2); const rectShape2 = service.getElementById(rectId2) as ShapeElementModel; expect(rectShape2.fillColor).toBe(ShapeFillColor.Orange); - const diamondId2 = service.addElement('shape', { + const diamondId2 = service.crud.addElement('shape', { shapeType: ShapeType.Diamond, }); + assertExists(diamondId2); const diamondShape2 = service.getElementById( diamondId2 ) as ShapeElementModel; expect(diamondShape2.fillColor).toBe(ShapeFillColor.Blue); - const roundedRectId2 = service.addElement('shape', { + const roundedRectId2 = service.crud.addElement('shape', { shapeType: ShapeType.Rect, radius: 0.1, }); + assertExists(roundedRectId2); const roundedRectShape2 = service.getElementById( roundedRectId2 ) as ShapeElementModel; @@ -110,26 +121,29 @@ describe('apply last props', () => { }); test('connector', () => { - const id = service.addElement('connector', { mode: 0 }); + const id = service.crud.addElement('connector', { mode: 0 }); + assertExists(id); const connector = service.getElementById(id) as ConnectorElementModel; expect(connector.stroke).toBe(LineColor.Grey); expect(connector.strokeWidth).toBe(2); expect(connector.strokeStyle).toBe('solid'); expect(connector.frontEndpointStyle).toBe('None'); expect(connector.rearEndpointStyle).toBe('Arrow'); - service.updateElement(id, { strokeWidth: 10 }); + service.crud.updateElement(id, { strokeWidth: 10 }); - const id2 = service.addElement('connector', { mode: 1 }); + const id2 = service.crud.addElement('connector', { mode: 1 }); + assertExists(id2); const connector2 = service.getElementById(id2) as ConnectorElementModel; expect(connector2.strokeWidth).toBe(10); - service.updateElement(id2, { + service.crud.updateElement(id2, { labelStyle: { color: LineColor.Magenta, fontFamily: FontFamily.Kalam, }, }); - const id3 = service.addElement('connector', { mode: 1 }); + const id3 = service.crud.addElement('connector', { mode: 1 }); + assertExists(id3); const connector3 = service.getElementById(id3) as ConnectorElementModel; expect(connector3.strokeWidth).toBe(10); expect(connector3.labelStyle.color).toBe(LineColor.Magenta); @@ -137,42 +151,46 @@ describe('apply last props', () => { }); test('brush', () => { - const id = service.addElement('brush', {}); + const id = service.crud.addElement('brush', {}); + assertExists(id); const brush = service.getElementById(id) as BrushElementModel; expect(brush.color).toEqual({ dark: LineColor.White, light: LineColor.Black, }); expect(brush.lineWidth).toBe(4); - service.updateElement(id, { lineWidth: 10 }); + service.crud.updateElement(id, { lineWidth: 10 }); const secondBrush = service.getElementById( - service.addElement('brush', {}) + service.crud.addElement('brush', {}) as string ) as BrushElementModel; expect(secondBrush.lineWidth).toBe(10); }); test('text', () => { - const id = service.addElement('text', {}); + const id = service.crud.addElement('text', {}); + assertExists(id); const text = service.getElementById(id) as TextElementModel; expect(text.fontSize).toBe(24); - service.updateElement(id, { fontSize: 36 }); + service.crud.updateElement(id, { fontSize: 36 }); const secondText = service.getElementById( - service.addElement('text', {}) + service.crud.addElement('text', {}) as string ) as TextElementModel; expect(secondText.fontSize).toBe(36); }); test('mindmap', () => { - const id = service.addElement('mindmap', {}); + const id = service.crud.addElement('mindmap', {}); + assertExists(id); const mindmap = service.getElementById(id) as MindmapElementModel; expect(mindmap.layoutType).toBe(LayoutType.RIGHT); expect(mindmap.style).toBe(MindmapStyle.ONE); - service.updateElement(id, { + service.crud.updateElement(id, { layoutType: LayoutType.BALANCE, style: MindmapStyle.THREE, }); - const id2 = service.addElement('mindmap', {}); + const id2 = service.crud.addElement('mindmap', {}); + assertExists(id2); const mindmap2 = service.getElementById(id2) as MindmapElementModel; expect(mindmap2.layoutType).toBe(LayoutType.BALANCE); expect(mindmap2.style).toBe(MindmapStyle.THREE); @@ -180,27 +198,30 @@ describe('apply last props', () => { test('edgeless-text', () => { const surface = getSurfaceBlock(doc); - const id = service.addBlock('affine:edgeless-text', {}, surface!.id); + const id = service.crud.addBlock('affine:edgeless-text', {}, surface!.id); + assertExists(id); const text = service.getElementById(id) as EdgelessTextBlockModel; expect(text.color).toBe(DEFAULT_TEXT_COLOR); expect(text.fontFamily).toBe(FontFamily.Inter); - service.updateElement(id, { + service.crud.updateElement(id, { color: LineColor.Green, fontFamily: FontFamily.OrelegaOne, }); - const id2 = service.addBlock('affine:edgeless-text', {}, surface!.id); + const id2 = service.crud.addBlock('affine:edgeless-text', {}, surface!.id); + assertExists(id2); const text2 = service.getElementById(id2) as EdgelessTextBlockModel; expect(text2.color).toBe(LineColor.Green); expect(text2.fontFamily).toBe(FontFamily.OrelegaOne); }); test('note', () => { - const id = service.addBlock('affine:note', {}, doc.root!.id); + const id = service.crud.addBlock('affine:note', {}, doc.root!.id); + assertExists(id); const note = service.getElementById(id) as NoteBlockModel; expect(note.background).toBe(DEFAULT_NOTE_BACKGROUND_COLOR); expect(note.edgeless.style.shadowType).toBe(DEFAULT_NOTE_SHADOW); - service.updateElement(id, { + service.crud.updateElement(id, { background: NoteBackgroundColor.Purple, edgeless: { style: { @@ -209,7 +230,8 @@ describe('apply last props', () => { }, }); - const id2 = service.addBlock('affine:note', {}, doc.root!.id); + const id2 = service.crud.addBlock('affine:note', {}, doc.root!.id); + assertExists(id2); const note2 = service.getElementById(id2) as NoteBlockModel; expect(note2.background).toBe(NoteBackgroundColor.Purple); expect(note2.edgeless.style.shadowType).toBe(NoteShadow.Film); @@ -217,28 +239,32 @@ describe('apply last props', () => { test('frame', () => { const surface = getSurfaceBlock(doc); - const id = service.addBlock('affine:frame', {}, surface!.id); + const id = service.crud.addBlock('affine:frame', {}, surface!.id); + assertExists(id); const note = service.getElementById(id) as FrameBlockModel; expect(note.background).toBe('--affine-palette-transparent'); - service.updateElement(id, { + service.crud.updateElement(id, { background: FrameBackgroundColor.Purple, }); - const id2 = service.addBlock('affine:frame', {}, surface!.id); + const id2 = service.crud.addBlock('affine:frame', {}, surface!.id); + assertExists(id2); const frame2 = service.getElementById(id2) as FrameBlockModel; expect(frame2.background).toBe(FrameBackgroundColor.Purple); - service.updateElement(id, { + service.crud.updateElement(id2, { background: { normal: '#def4e740' }, }); - const id3 = service.addBlock('affine:frame', {}, surface!.id); + const id3 = service.crud.addBlock('affine:frame', {}, surface!.id); + assertExists(id3); const frame3 = service.getElementById(id3) as FrameBlockModel; expect(frame3.background).toEqual({ normal: '#def4e740' }); - service.updateElement(id, { + service.crud.updateElement(id3, { background: { light: '#a381aa23', dark: '#6e907452' }, }); - const id4 = service.addBlock('affine:frame', {}, surface!.id); + const id4 = service.crud.addBlock('affine:frame', {}, surface!.id); + assertExists(id4); const frame4 = service.getElementById(id4) as FrameBlockModel; expect(frame4.background).toEqual({ light: '#a381aa23', diff --git a/blocksuite/presets/src/__tests__/edgeless/layer.spec.ts b/blocksuite/presets/src/__tests__/edgeless/layer.spec.ts index c6a12a8fc2..8e0f69847b 100644 --- a/blocksuite/presets/src/__tests__/edgeless/layer.spec.ts +++ b/blocksuite/presets/src/__tests__/edgeless/layer.spec.ts @@ -45,7 +45,7 @@ describe('add new edgeless blocks or canvas elements should update layer automat test('add note, note, shape sequentially', async () => { addNote(doc); addNote(doc); - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', }); @@ -56,7 +56,7 @@ describe('add new edgeless blocks or canvas elements should update layer automat test('add note, shape, note sequentially', async () => { addNote(doc); - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', }); addNote(doc); @@ -68,7 +68,7 @@ describe('add new edgeless blocks or canvas elements should update layer automat test('delete element should update layer automatically', () => { const id = addNote(doc); - const canvasElId = service.addElement('shape', { + const canvasElId = service.crud.addElement('shape', { shapeType: 'rect', }); @@ -76,20 +76,20 @@ test('delete element should update layer automatically', () => { expect(service.layer.layers.length).toBe(1); - service.removeElement(canvasElId); + service.removeElement(canvasElId!); expect(service.layer.layers.length).toBe(0); }); test('change element should update layer automatically', async () => { const id = addNote(doc); - const canvasElId = service.addElement('shape', { + const canvasElId = service.crud.addElement('shape', { shapeType: 'rect', }); await wait(); - service.updateElement(id, { + service.crud.updateElement(id, { index: service.layer.getReorderedIndex( service.getElementById(id)!, 'forward' @@ -99,9 +99,9 @@ test('change element should update layer automatically', async () => { 'block' ); - service.updateElement(canvasElId, { + service.crud.updateElement(canvasElId!, { index: service.layer.getReorderedIndex( - service.getElementById(canvasElId)!, + service.getElementById(canvasElId!)!, 'forward' ), }); @@ -112,7 +112,7 @@ test('change element should update layer automatically', async () => { test('new added canvas elements should be placed in the topmost canvas layer', async () => { addNote(doc); - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', }); @@ -130,11 +130,11 @@ test("there should be at lease one layer in canvasLayers property even there's n test('if the topmost layer is canvas layer, the length of canvasLayers array should equal to the counts of canvas layers', () => { addNote(doc); - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', }); addNote(doc); - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', }); @@ -145,11 +145,11 @@ test('if the topmost layer is canvas layer, the length of canvasLayers array sho }); test('a new layer should be created in canvasLayers prop when the topmost layer is not canvas layer', () => { - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', }); addNote(doc); - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', }); addNote(doc); @@ -162,13 +162,13 @@ test('layer zindex should update correctly when elements changed', async () => { const noteId = addNote(doc); const note = service.getElementById(noteId); addNote(doc); - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', }); - const topShapeId = service.addElement('shape', { + const topShapeId = service.crud.addElement('shape', { shapeType: 'rect', }); - const topShape = service.getElementById(topShapeId); + const topShape = service.getElementById(topShapeId!)!; await wait(); @@ -183,7 +183,7 @@ test('layer zindex should update correctly when elements changed', async () => { service.doc.captureSync(); - service.updateElement(noteId, { + service.crud.updateElement(noteId, { index: service.layer.getReorderedIndex(note!, 'front'), }); await wait(); @@ -198,7 +198,7 @@ test('layer zindex should update correctly when elements changed', async () => { }; assert2StepState(); - service.updateElement(topShapeId, { + service.crud.updateElement(topShapeId!, { index: service.layer.getReorderedIndex(topShape!, 'front'), }); await wait(); @@ -235,7 +235,7 @@ test('blocks should rerender when their z-index changed', async () => { await wait(); assertBlocksContent(); - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', index: CommonUtils.generateKeyBetween( service.getElementById(blocks[1])!.index, @@ -252,19 +252,19 @@ describe('layer reorder functionality', () => { beforeEach(() => { ids = [ - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', - }), + })!, addNote(doc), - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', - }), + })!, addNote(doc), ]; }); test('forward', async () => { - service.updateElement(ids[0], { + service.crud.updateElement(ids[0], { index: service.layer.getReorderedIndex( service.getElementById(ids[0])!, 'forward' @@ -284,7 +284,7 @@ describe('layer reorder functionality', () => { await wait(); - service.updateElement(ids[1], { + service.crud.updateElement(ids[1], { index: service.layer.getReorderedIndex( service.getElementById(ids[1])!, 'forward' @@ -304,7 +304,7 @@ describe('layer reorder functionality', () => { }); test('front', async () => { - service.updateElement(ids[0], { + service.crud.updateElement(ids[0], { index: service.layer.getReorderedIndex( service.getElementById(ids[0])!, 'front' @@ -319,7 +319,7 @@ describe('layer reorder functionality', () => { ) ).toBe(3); - service.updateElement(ids[1], { + service.crud.updateElement(ids[1], { index: service.layer.getReorderedIndex( service.getElementById(ids[1])!, 'front' @@ -334,7 +334,7 @@ describe('layer reorder functionality', () => { }); test('backward', async () => { - service.updateElement(ids[3], { + service.crud.updateElement(ids[3], { index: service.layer.getReorderedIndex( service.getElementById(ids[3])!, 'backward' @@ -354,7 +354,7 @@ describe('layer reorder functionality', () => { await wait(); - service.updateElement(ids[2], { + service.crud.updateElement(ids[2], { index: service.layer.getReorderedIndex( service.getElementById(ids[2])!, 'backward' @@ -374,7 +374,7 @@ describe('layer reorder functionality', () => { }); test('back', async () => { - service.updateElement(ids[3], { + service.crud.updateElement(ids[3], { index: service.layer.getReorderedIndex( service.getElementById(ids[3])!, 'back' @@ -389,7 +389,7 @@ describe('layer reorder functionality', () => { await wait(); - service.updateElement(ids[2], { + service.crud.updateElement(ids[2], { index: service.layer.getReorderedIndex( service.getElementById(ids[2])!, 'back' @@ -412,7 +412,7 @@ describe('group related functionality', () => { const children = new DocCollection.Y.Map(); childIds.forEach(id => children.set(id, true)); - return service.addElement('group', { + return service.crud.addElement('group', { children, }); }; @@ -420,17 +420,17 @@ describe('group related functionality', () => { test("new added group should effect it children's layer", async () => { const edgeless = getDocRootBlock(doc, editor, 'edgeless'); const elements = [ - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', - }), + })!, addNote(doc), - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', - }), + })!, addNote(doc), - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', - }), + })!, ]; await wait(0); @@ -490,23 +490,23 @@ describe('group related functionality', () => { test("change group index should update its children's layer", () => { const elements = [ - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', - }), + })!, addNote(doc), - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', - }), + })!, addNote(doc), - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', - }), + })!, ]; const groupId = createGroup( service, elements.filter((_, idx) => idx !== 1 && idx !== 3) - ); + )!; const group = service.getElementById(groupId)!; expect(service.layer.layers.length).toBe(2); @@ -525,17 +525,17 @@ describe('group related functionality', () => { test('should keep relative index order of elements after group, ungroup, undo, redo', () => { const edgeless = getDocRootBlock(doc, editor, 'edgeless'); const elementIds = [ - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', - }), + })!, addNote(doc), - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', - }), + })!, addNote(doc), - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', - }), + })!, ]; service.doc.captureSync(); const elements = elementIds.map(id => service.getElementById(id)!); @@ -549,7 +549,7 @@ describe('group related functionality', () => { expect(isKeptRelativeOrder()).toBeTruthy(); - const groupId = createGroup(edgeless.service, elementIds); + const groupId = createGroup(edgeless.service, elementIds)!; expect(isKeptRelativeOrder()).toBeTruthy(); service.ungroup(service.getElementById(groupId) as GroupElementModel); @@ -577,19 +577,19 @@ describe('compare function', () => { const children = new DocCollection.Y.Map(); childIds.forEach(id => children.set(id, true)); - return service.addElement('group', { + return service.crud.addElement('group', { children, }); }; test('compare same element', () => { - const shapeId = service.addElement('shape', { + const shapeId = service.crud.addElement('shape', { shapeType: 'rect', - }); + })!; const shapeEl = service.getElementById(shapeId)!; expect(service.layer.compare(shapeEl, shapeEl)).toBe(SORT_ORDER.SAME); - const groupId = createGroup(service, [shapeId]); + const groupId = createGroup(service, [shapeId])!; const groupEl = service.getElementById(groupId)!; expect(service.layer.compare(groupEl, groupEl)).toBe(SORT_ORDER.SAME); @@ -599,13 +599,13 @@ describe('compare function', () => { }); test('compare a group and its child', () => { - const shapeId = service.addElement('shape', { + const shapeId = service.crud.addElement('shape', { shapeType: 'rect', - }); + })!; const shapeEl = service.getElementById(shapeId)!; const noteId = addNote(doc); const note = service.getElementById(noteId)! as NoteBlockModel; - const groupId = createGroup(service, [shapeId, noteId]); + const groupId = createGroup(service, [shapeId, noteId])!; const groupEl = service.getElementById(groupId)!; expect(service.layer.compare(groupEl, shapeEl)).toBe(SORT_ORDER.BEFORE); @@ -615,13 +615,13 @@ describe('compare function', () => { }); test('compare two different elements', () => { - const shape1Id = service.addElement('shape', { + const shape1Id = service.crud.addElement('shape', { shapeType: 'rect', - }); + })!; const shape1 = service.getElementById(shape1Id)!; - const shape2Id = service.addElement('shape', { + const shape2Id = service.crud.addElement('shape', { shapeType: 'rect', - }); + })!; const shape2 = service.getElementById(shape2Id)!; const note1Id = addNote(doc); @@ -643,12 +643,12 @@ describe('compare function', () => { }); test('compare nested elements', () => { - const shape1Id = service.addElement('shape', { + const shape1Id = service.crud.addElement('shape', { shapeType: 'rect', - }); - const shape2Id = service.addElement('shape', { + })!; + const shape2Id = service.crud.addElement('shape', { shapeType: 'rect', - }); + })!; const note1Id = addNote(doc); const note2Id = addNote(doc); const group1Id = createGroup(service, [ @@ -656,8 +656,8 @@ describe('compare function', () => { shape2Id, note1Id, note2Id, - ]); - const group2Id = createGroup(service, [group1Id]); + ])!; + const group2Id = createGroup(service, [group1Id])!; const shape1 = service.getElementById(shape1Id)!; const shape2 = service.getElementById(shape2Id)!; @@ -688,24 +688,24 @@ describe('compare function', () => { }); test('compare two nested elements', () => { - const groupAShapeId = service.addElement('shape', { + const groupAShapeId = service.crud.addElement('shape', { shapeType: 'rect', - }); + })!; const groupANoteId = addNote(doc); const groupAId = createGroup(service, [ - createGroup(service, [groupAShapeId, groupANoteId]), - ]); + createGroup(service, [groupAShapeId, groupANoteId])!, + ])!; const groupAShape = service.getElementById(groupAShapeId)!; const groupANote = service.getElementById(groupANoteId)!; const groupA = service.getElementById(groupAId)!; - const groupBShapeId = service.addElement('shape', { + const groupBShapeId = service.crud.addElement('shape', { shapeType: 'rect', - }); + })!; const groupBNoteId = addNote(doc); const groupBId = createGroup(service, [ - createGroup(service, [groupBShapeId, groupBNoteId]), - ]); + createGroup(service, [groupBShapeId, groupBNoteId])!, + ])!; const groupBShape = service.getElementById(groupBShapeId)!; const groupBNote = service.getElementById(groupBNoteId)!; const groupB = service.getElementById(groupBId)!; @@ -759,17 +759,17 @@ describe('compare function', () => { test('indexed canvas should be inserted into edgeless portal when switch to edgeless mode', async () => { let surface = getSurface(doc, editor); - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', - }); + })!; addNote(doc); await wait(); - service.addElement('shape', { + service.crud.addElement('shape', { shapeType: 'rect', - }); + })!; editor.mode = 'page'; await wait(); @@ -855,9 +855,9 @@ describe('index generator', () => { let preinsertedNote: NoteBlockModel; beforeEach(() => { - const shapeId = service.addElement('shape', { + const shapeId = service.crud.addElement('shape', { shapeType: 'rect', - }); + })!; const noteId = addNote(doc); preinsertedShape = service.getElementById( diff --git a/blocksuite/presets/src/__tests__/edgeless/surface-ref.spec.ts b/blocksuite/presets/src/__tests__/edgeless/surface-ref.spec.ts index a4af373a8b..b6040e9ed0 100644 --- a/blocksuite/presets/src/__tests__/edgeless/surface-ref.spec.ts +++ b/blocksuite/presets/src/__tests__/edgeless/surface-ref.spec.ts @@ -29,20 +29,20 @@ describe('basic', () => { noteAId = addNote(doc, { index: service.generateIndex(), }); - shapeAId = service.addElement('shape', { + shapeAId = service.crud.addElement('shape', { type: 'rect', xywh: '[0, 0, 100, 100]', index: service.generateIndex(), - }); + })!; noteBId = addNote(doc, { index: service.generateIndex(), }); - shapeBId = service.addElement('shape', { + shapeBId = service.crud.addElement('shape', { type: 'rect', xywh: '[100, 0, 100, 100]', index: service.generateIndex(), - }); - frameId = service.addBlock( + })!; + frameId = service.crud.addBlock( 'affine:frame', { xywh: '[0, 0, 800, 200]', @@ -122,7 +122,7 @@ describe('basic', () => { }); test('content in group should be rendered in the correct order', async () => { - const groupId = service.addElement('group', { + const groupId = service.crud.addElement('group', { children: { [shapeAId]: true, [shapeBId]: true, @@ -186,14 +186,14 @@ describe('basic', () => { }); test('group should be rendered in surface-ref viewport', async () => { - const groupId = service.addElement('group', { + const groupId = service.crud.addElement('group', { children: { [shapeAId]: true, [shapeBId]: true, [noteAId]: true, [noteBId]: true, }, - }); + })!; const surfaceRefId = doc.addBlock( 'affine:surface-ref', { @@ -247,7 +247,7 @@ describe('basic', () => { }); test('view in edgeless mode button', async () => { - const groupId = service.addElement('group', { + const groupId = service.crud.addElement('group', { children: { [shapeAId]: true, [shapeBId]: true, diff --git a/blocksuite/presets/src/__tests__/edgeless/tools.spec.ts b/blocksuite/presets/src/__tests__/edgeless/tools.spec.ts index 856332bb78..aab77dfc14 100644 --- a/blocksuite/presets/src/__tests__/edgeless/tools.spec.ts +++ b/blocksuite/presets/src/__tests__/edgeless/tools.spec.ts @@ -26,7 +26,7 @@ describe('default tool', () => { }); test('element click selection', async () => { - const id = service.addElement('shape', { + const id = service.crud.addElement('shape', { shapeType: 'rect', xywh: '[0,0,100,100]', fillColor: 'red', @@ -47,7 +47,7 @@ describe('default tool', () => { }); test('element drag moving', async () => { - const id = edgeless.service.addElement('shape', { + const id = edgeless.service.crud.addElement('shape', { shapeType: 'rect', xywh: '[0,0,100,100]', fillColor: 'red', @@ -64,7 +64,7 @@ describe('default tool', () => { drag(edgeless.host, { x: 0, y: 50 }, { x: 0, y: 150 }); await wait(); - const element = service.getElementById(id)!; + const element = service.getElementById(id!)!; expect(element.xywh).toEqual(`[0,100,100,100]`); }); diff --git a/packages/frontend/core/src/blocksuite/presets/ai/_common/chat-actions-handle.ts b/packages/frontend/core/src/blocksuite/presets/ai/_common/chat-actions-handle.ts index c2170cfdb3..34c1ecc553 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/_common/chat-actions-handle.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/_common/chat-actions-handle.ts @@ -508,7 +508,7 @@ const CREATE_AS_LINKED_DOC = { y = viewportCenter.y - height / 2; } - service.addBlock( + service.crud.addBlock( 'affine:embed-linked-doc', { xywh: `[${x}, ${y}, ${width}, ${height}]`, diff --git a/packages/frontend/core/src/blocksuite/presets/ai/peek-view/chat-block-peek-view.ts b/packages/frontend/core/src/blocksuite/presets/ai/peek-view/chat-block-peek-view.ts index ebbf308a30..d2e4ac2cc3 100644 --- a/packages/frontend/core/src/blocksuite/presets/ai/peek-view/chat-block-peek-view.ts +++ b/packages/frontend/core/src/blocksuite/presets/ai/peek-view/chat-block-peek-view.ts @@ -174,7 +174,7 @@ export class AIChatBlockPeekView extends LitElement { const edgelessService = this._rootService as EdgelessRootService; const bound = calcChildBound(this.parentModel, edgelessService); - const aiChatBlockId = edgelessService.addBlock( + const aiChatBlockId = edgelessService.crud.addBlock( 'affine:embed-ai-chat' as keyof BlockSuite.BlockModels, { xywh: bound.serialize(), @@ -193,7 +193,7 @@ export class AIChatBlockPeekView extends LitElement { this.updateContext({ currentChatBlockId: aiChatBlockId }); // Connect the parent chat block to the AI chat block - edgelessService.addElement(CanvasElementType.CONNECTOR, { + edgelessService.crud.addElement(CanvasElementType.CONNECTOR, { mode: ConnectorMode.Curve, controllers: [], source: { id: this.parentChatBlockId }, diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx index 26798f5275..7a991a7e8e 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx @@ -530,7 +530,7 @@ export function patchEdgelessClipboard() { rootDocId, rootWorkspaceId, } = block.props; - const blockId = component.service.addBlock( + const blockId = component.service.crud.addBlock( AIChatBlockFlavour, { xywh, diff --git a/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/shape.tsx b/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/shape.tsx index facfa98d7a..281628135e 100644 --- a/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/shape.tsx +++ b/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/shape.tsx @@ -361,7 +361,7 @@ export const ShapeSettings = () => { const { shapeType, radius } = shape; const shapeName = getShapeName(shapeType, radius); const props = editorSetting.get(`shape:${shapeName}`); - edgelessService.updateElement(shape.id, props); + edgelessService.crud.updateElement(shape.id, props); }); doc.awarenessStore.setReadonly(doc.blockCollection, true); }, diff --git a/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/snapshot.tsx b/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/snapshot.tsx index 81e0d94dac..329753c298 100644 --- a/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/snapshot.tsx +++ b/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/snapshot.tsx @@ -79,7 +79,7 @@ export const EdgelessSnapshot = (props: Props) => { const props = editorSetting.get(keyName) as any; doc.awarenessStore.setReadonly(doc.blockCollection, false); elements.forEach(element => { - edgelessService.updateElement(element.id, props); + edgelessService.crud.updateElement(element.id, props); }); doc.awarenessStore.setReadonly(doc.blockCollection, true); }, [editorSetting, getElements, keyName]);