fix(editor): limit max width when uploading or pasting image into edgeless (#9273)

[BS-2180](https://linear.app/affine-design/issue/BS-2180/白板中粘贴图片,限定一下最大宽度)
This commit is contained in:
donteatfriedrice
2024-12-24 06:28:59 +00:00
parent 4ce5cf20c3
commit 338835a4aa
7 changed files with 34 additions and 14 deletions

View File

@@ -0,0 +1 @@
export const MAX_IMAGE_WIDTH = 460;

View File

@@ -2,6 +2,7 @@ export * from './brush.js';
export * from './connector.js'; export * from './connector.js';
export * from './doc.js'; export * from './doc.js';
export * from './frame.js'; export * from './frame.js';
export * from './image.js';
export * from './line.js'; export * from './line.js';
export * from './mindmap.js'; export * from './mindmap.js';
export * from './note.js'; export * from './note.js';

View File

@@ -1,5 +1,5 @@
import { FileDropConfigExtension } from '@blocksuite/affine-components/drag-indicator'; import { FileDropConfigExtension } from '@blocksuite/affine-components/drag-indicator';
import { ImageBlockSchema } from '@blocksuite/affine-model'; import { ImageBlockSchema, MAX_IMAGE_WIDTH } from '@blocksuite/affine-model';
import { import {
DragHandleConfigExtension, DragHandleConfigExtension,
TelemetryProvider, TelemetryProvider,
@@ -52,7 +52,9 @@ export const ImageDropOption = FileDropConfigExtension({
if (isInsideEdgelessEditor(std.host)) { if (isInsideEdgelessEditor(std.host)) {
const gfx = std.get(GfxControllerIdentifier); const gfx = std.get(GfxControllerIdentifier);
point = gfx.viewport.toViewCoordFromClientCoord(point); point = gfx.viewport.toViewCoordFromClientCoord(point);
addImages(std, files, point).catch(console.error); addImages(std, files, { point, maxWidth: MAX_IMAGE_WIDTH }).catch(
console.error
);
std.getOptional(TelemetryProvider)?.track('CanvasElementAdded', { std.getOptional(TelemetryProvider)?.track('CanvasElementAdded', {
control: 'canvas:drop', control: 'canvas:drop',

View File

@@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { import {
CanvasElementType, CanvasElementType,
SurfaceGroupLikeModel, SurfaceGroupLikeModel,
@@ -9,6 +8,7 @@ import {
BookmarkStyles, BookmarkStyles,
DEFAULT_NOTE_HEIGHT, DEFAULT_NOTE_HEIGHT,
DEFAULT_NOTE_WIDTH, DEFAULT_NOTE_WIDTH,
MAX_IMAGE_WIDTH,
ReferenceInfoSchema, ReferenceInfoSchema,
} from '@blocksuite/affine-model'; } from '@blocksuite/affine-model';
import { import {
@@ -256,7 +256,10 @@ export class EdgelessClipboardController extends PageClipboard {
// when only images in clipboard, add image-blocks else add all files as attachments // when only images in clipboard, add image-blocks else add all files as attachments
if (attachmentFiles.length === 0) { if (attachmentFiles.length === 0) {
await addImages(this.std, imageFiles, point); await addImages(this.std, imageFiles, {
point,
maxWidth: MAX_IMAGE_WIDTH,
});
} else { } else {
await addAttachments(this.std, [...files], point); await addAttachments(this.std, [...files], point);
} }
@@ -359,7 +362,7 @@ export class EdgelessClipboardController extends PageClipboard {
const svg = tryGetSvgFromClipboard(data); const svg = tryGetSvgFromClipboard(data);
if (svg) { if (svg) {
await addImages(this.std, [svg], point); await addImages(this.std, [svg], { point, maxWidth: MAX_IMAGE_WIDTH });
return; return;
} }
try { try {

View File

@@ -1,4 +1,5 @@
import { AttachmentIcon, LinkIcon } from '@blocksuite/affine-components/icons'; import { AttachmentIcon, LinkIcon } from '@blocksuite/affine-components/icons';
import { MAX_IMAGE_WIDTH } from '@blocksuite/affine-model';
import { TelemetryProvider } from '@blocksuite/affine-shared/services'; import { TelemetryProvider } from '@blocksuite/affine-shared/services';
import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx'; import type { GfxToolsFullOptionValue } from '@blocksuite/block-std/gfx';
import { effect } from '@preact/signals-core'; import { effect } from '@preact/signals-core';
@@ -55,7 +56,9 @@ export class EdgelessNoteMenu extends EdgelessToolbarToolMixin(LitElement) {
private async _addImages() { private async _addImages() {
this._imageLoading = true; this._imageLoading = true;
const imageFiles = await getImageFilesFromLocal(); const imageFiles = await getImageFilesFromLocal();
const ids = await addImages(this.edgeless.std, imageFiles); const ids = await addImages(this.edgeless.std, imageFiles, {
maxWidth: MAX_IMAGE_WIDTH,
});
this._imageLoading = false; this._imageLoading = false;
this.edgeless.gfx.tool.setTool('default'); this.edgeless.gfx.tool.setTool('default');
this.edgeless.gfx.selection.set({ elements: ids }); this.edgeless.gfx.selection.set({ elements: ids });

View File

@@ -118,7 +118,10 @@ export async function addAttachments(
export async function addImages( export async function addImages(
std: BlockStdScope, std: BlockStdScope,
files: File[], files: File[],
point?: IVec options: {
point?: IVec;
maxWidth?: number;
}
): Promise<string[]> { ): Promise<string[]> {
const imageFiles = [...files].filter(file => file.type.startsWith('image/')); const imageFiles = [...files].filter(file => file.type.startsWith('image/'));
if (!imageFiles.length) return []; if (!imageFiles.length) return [];
@@ -145,6 +148,7 @@ export async function addImages(
return []; return [];
} }
const { point, maxWidth } = options;
let { x, y } = gfx.viewport.center; let { x, y } = gfx.viewport.center;
if (point) [x, y] = gfx.viewport.toModelCoord(...point); if (point) [x, y] = gfx.viewport.toModelCoord(...point);
@@ -166,6 +170,7 @@ export async function addImages(
{ {
size: file.size, size: file.size,
xywh: bound.serialize(), xywh: bound.serialize(),
index: gfx.layer.generateIndex(),
}, },
gfx.surface gfx.surface
); );
@@ -180,17 +185,22 @@ export async function addImages(
const imageSize = await readImageSize(file); const imageSize = await readImageSize(file);
const center = Vec.toVec(point); const center = Vec.toVec(point);
const bound = calcBoundByOrigin( // If maxWidth is provided, limit the width of the image to maxWidth
center, // Otherwise, use the original width
inTopLeft, const width = maxWidth
imageSize.width, ? Math.min(imageSize.width, maxWidth)
imageSize.height : imageSize.width;
); const height = maxWidth
? (imageSize.height / imageSize.width) * width
: imageSize.height;
const bound = calcBoundByOrigin(center, inTopLeft, width, height);
std.doc.withoutTransact(() => { std.doc.withoutTransact(() => {
gfx.updateElement(blockId, { gfx.updateElement(blockId, {
sourceId, sourceId,
...imageSize, ...imageSize,
width,
height,
xywh: bound.serialize(), xywh: bound.serialize(),
} satisfies Partial<ImageBlockProps>); } satisfies Partial<ImageBlockProps>);
}); });

View File

@@ -339,7 +339,7 @@ function responseToCreateImage(host: EditorHost) {
const [x, y] = edgelessRoot.service.viewport.toViewCoord(minX, minY); const [x, y] = edgelessRoot.service.viewport.toViewCoord(minX, minY);
host.doc.transact(() => { host.doc.transact(() => {
addImages(edgelessRoot.std, [img], [x, y]) addImages(edgelessRoot.std, [img], { point: [x, y] })
.then(blockIds => { .then(blockIds => {
const imageBlockId = blockIds[0]; const imageBlockId = blockIds[0];
const imageBlock = host.doc.getBlock(imageBlockId); const imageBlock = host.doc.getBlock(imageBlockId);