From 6b4a5b1d71b1a4b8f460cfa9455de4434bb84c03 Mon Sep 17 00:00:00 2001 From: L-Sun Date: Wed, 26 Mar 2025 08:57:38 +0000 Subject: [PATCH] fix(editor): missing connectors when click copy as image action in surface-ref-toolbar (#11137) Continue #10933 --- .../block-surface-ref/src/widgets/config.ts | 4 +- .../block-surface-ref/src/widgets/utils.ts | 6 +- .../export-manager/export-manager.ts | 136 +++++------------- 3 files changed, 39 insertions(+), 107 deletions(-) diff --git a/blocksuite/affine/blocks/block-surface-ref/src/widgets/config.ts b/blocksuite/affine/blocks/block-surface-ref/src/widgets/config.ts index 80767a34c5..2365477797 100644 --- a/blocksuite/affine/blocks/block-surface-ref/src/widgets/config.ts +++ b/blocksuite/affine/blocks/block-surface-ref/src/widgets/config.ts @@ -44,7 +44,7 @@ export const BUILT_IN_GROUPS: MenuItemGroup[] = [ return; } - edgelessToBlob(ctx.host, { + edgelessToBlob(editor, { surfaceRefBlock: ctx.blockComponent, surfaceRenderer, edgelessElement: referencedModel, @@ -84,7 +84,7 @@ export const BUILT_IN_GROUPS: MenuItemGroup[] = [ return; } - edgelessToBlob(ctx.host, { + edgelessToBlob(editor, { surfaceRefBlock: ctx.blockComponent, surfaceRenderer, edgelessElement: referencedModel, diff --git a/blocksuite/affine/blocks/block-surface-ref/src/widgets/utils.ts b/blocksuite/affine/blocks/block-surface-ref/src/widgets/utils.ts index 30970f24d2..7be9fe4edd 100644 --- a/blocksuite/affine/blocks/block-surface-ref/src/widgets/utils.ts +++ b/blocksuite/affine/blocks/block-surface-ref/src/widgets/utils.ts @@ -1,7 +1,6 @@ import type { CanvasRenderer } from '@blocksuite/affine-block-surface'; import { ExportManager } from '@blocksuite/affine-block-surface'; import type { SurfaceRefBlockComponent } from '@blocksuite/affine-block-surface-ref'; -import { isTopLevelBlock } from '@blocksuite/affine-shared/utils'; import type { EditorHost } from '@blocksuite/block-std'; import { GfxControllerIdentifier, @@ -21,15 +20,14 @@ export const edgelessToBlob = async ( const { edgelessElement } = options; const exportManager = host.std.get(ExportManager); const bound = Bound.deserialize(edgelessElement.xywh); - const isBlock = isTopLevelBlock(edgelessElement); const gfx = host.std.get(GfxControllerIdentifier); const canvas = await exportManager.edgelessToCanvas( options.surfaceRenderer, bound, gfx, - isBlock ? [edgelessElement] : undefined, - isBlock ? undefined : [edgelessElement], + undefined, + undefined, { zoom: options.surfaceRenderer.viewport.zoom } ); diff --git a/blocksuite/affine/blocks/block-surface/src/extensions/export-manager/export-manager.ts b/blocksuite/affine/blocks/block-surface/src/extensions/export-manager/export-manager.ts index 0dd73afac9..dd2c384b04 100644 --- a/blocksuite/affine/blocks/block-surface/src/extensions/export-manager/export-manager.ts +++ b/blocksuite/affine/blocks/block-surface/src/extensions/export-manager/export-manager.ts @@ -1,9 +1,4 @@ -import { - FrameBlockModel, - ImageBlockModel, - type NoteBlockModel, - type RootBlockModel, -} from '@blocksuite/affine-model'; +import { ImageBlockModel, type RootBlockModel } from '@blocksuite/affine-model'; import { FetchUtils } from '@blocksuite/affine-shared/adapters'; import { CANVAS_EXPORT_IGNORE_TAGS, @@ -24,15 +19,15 @@ import { GfxBlockElementModel, type GfxController, GfxControllerIdentifier, - type GfxPrimitiveElementModel, + type GfxModel, + GfxPrimitiveElementModel, isGfxGroupCompatibleModel, } from '@blocksuite/block-std/gfx'; import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions'; import type { IBound } from '@blocksuite/global/gfx'; -import { Bound, deserializeXYWH } from '@blocksuite/global/gfx'; +import { deserializeXYWH } from '@blocksuite/global/gfx'; import type { ExtensionType, Store } from '@blocksuite/store'; -import { SurfaceElementModel } from '../../element-model/base.js'; import type { CanvasRenderer } from '../../renderer/canvas-renderer.js'; import type { SurfaceBlockComponent } from '../../surface-block.js'; import { getBgGridGap } from '../../utils/get-bg-grip-gap.js'; @@ -411,8 +406,8 @@ export class ExportManager { surfaceRenderer: CanvasRenderer, bound: IBound, gfx: GfxController, - nodes?: GfxBlockElementModel[], - surfaces?: GfxPrimitiveElementModel[], + blocks?: GfxBlockElementModel[], + elements?: GfxPrimitiveElementModel[], edgelessBackground?: { zoom: number; } @@ -428,10 +423,6 @@ export class ExportManager { if (!viewportElement) return; const containerComputedStyle = window.getComputedStyle(viewportElement); - const html2canvas = (element: HTMLElement) => - this._html2canvas(element, { - backgroundColor: containerComputedStyle.backgroundColor, - }); const container = rootComponent.querySelector( '.affine-block-children-container' ); @@ -455,8 +446,33 @@ export class ExportManager { }); } - const blocks = - nodes ?? gfx.getElementsByBound(bound, { type: 'block' }) ?? []; + if (!blocks && !elements) { + blocks = gfx.getElementsByBound(bound, { type: 'block' }); + elements = gfx.getElementsByBound(bound, { type: 'canvas' }); + } else { + elements = elements ?? []; + blocks = blocks ?? []; + const blockSet = new Set(); + const elementSet = new Set(); + const addFn = (item: GfxModel) => { + if (item instanceof GfxBlockElementModel) { + blockSet.add(item); + } else if (item instanceof GfxPrimitiveElementModel) { + elementSet.add(item); + } + }; + [...elements, ...blocks].forEach(item => { + addFn(item); + if (isGfxGroupCompatibleModel(item)) { + item.descendantElements.forEach(descendant => { + addFn(descendant); + }); + } + }); + elements = [...elementSet]; + blocks = [...blockSet]; + } + for (const block of blocks) { if (matchModels(block, [ImageBlockModel])) { if (!block.props.sourceId) return; @@ -495,49 +511,11 @@ export class ExportManager { ); } - if (matchModels(block, [FrameBlockModel])) { - // TODO(@L-Sun): use children of frame instead of bound - const blocksInsideFrame = getBlocksInFrameBound(this.doc, block, false); - const frameBound = Bound.deserialize(block.xywh); - - // eslint-disable-next-line @typescript-eslint/prefer-for-of - for (let i = 0; i < blocksInsideFrame.length; i++) { - const element = blocksInsideFrame[i]; - const htmlElement = this.editorHost.view.getBlock(block.id); - const blockBound = xywhArrayToObject(element); - const canvasData = await html2canvas(htmlElement as HTMLElement); - - ctx.drawImage( - canvasData, - blockBound.x - bound.x + 50, - blockBound.y - bound.y + 50, - blockBound.w, - (blockBound.w / canvasData.width) * canvasData.height - ); - } - const surfaceCanvas = surfaceRenderer.getCanvasByBound(frameBound); - - ctx.drawImage(surfaceCanvas, 50, 50, frameBound.w, frameBound.h); - } - this._checkCanContinueToCanvas(pathname, editorMode); } - if (surfaces?.length) { - const surfaceElements = surfaces.flatMap(element => - isGfxGroupCompatibleModel(element) - ? (element.descendantElements.filter( - el => el instanceof SurfaceElementModel - ) as SurfaceElementModel[]) - : element - ); - const surfaceCanvas = surfaceRenderer.getCanvasByBound( - bound, - surfaceElements - ); - - ctx.drawImage(surfaceCanvas, 50, 50, bound.w, bound.h); - } + const surfaceCanvas = surfaceRenderer.getCanvasByBound(bound, elements); + ctx.drawImage(surfaceCanvas, 50, 50, bound.w, bound.h); return canvas; } @@ -595,50 +573,6 @@ function xywhArrayToObject(element: GfxBlockElementModel) { return { x, y, w, h }; } -function getNotesInFrameBound( - doc: Store, - frame: FrameBlockModel, - fullyContained: boolean = true -): NoteBlockModel[] { - const bound = Bound.deserialize(frame.xywh); - - return (doc.getModelsByFlavour('affine:note') as NoteBlockModel[]).filter( - ele => { - const xywh = Bound.deserialize(ele.xywh); - - return fullyContained - ? bound.contains(xywh) - : bound.isPointInBound([xywh.x, xywh.y]); - } - ); -} - -function getBlocksInFrameBound( - doc: Store, - model: FrameBlockModel, - fullyContained: boolean = true -) { - const bound = Bound.deserialize(model.xywh); - const surface = model.surface; - if (!surface) return []; - - return ( - getNotesInFrameBound(doc, model, fullyContained) as GfxBlockElementModel[] - ).concat( - surface.children.filter((ele): ele is GfxBlockElementModel => { - if (ele.id === model.id) return false; - if (ele instanceof GfxBlockElementModel) { - const blockBound = Bound.deserialize(ele.xywh); - return fullyContained - ? bound.contains(blockBound) - : bound.containsPoint([blockBound.x, blockBound.y]); - } - - return false; - }) - ); -} - type RootBlockComponent = BlockComponent & { viewportElement: HTMLElement; viewport: Viewport;