refactor(editor): add native clipboard extension (#9502)

Closes: [BS-2239](https://linear.app/affine-design/issue/BS-2239/不应该通过window注入affine的api)
This commit is contained in:
fundon
2025-01-06 15:36:43 +00:00
parent f778d1a28a
commit 69e73af2a8
9 changed files with 117 additions and 36 deletions

View File

@@ -4,6 +4,7 @@ import type {
ImageBlockModel,
ImageBlockProps,
} from '@blocksuite/affine-model';
import { NativeClipboardProvider } from '@blocksuite/affine-shared/services';
import {
downloadBlob,
humanFileSize,
@@ -12,7 +13,6 @@ import {
} from '@blocksuite/affine-shared/utils';
import type { BlockStdScope, EditorHost } from '@blocksuite/block-std';
import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx';
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
import { Bound, type IVec, Point, Vec } from '@blocksuite/global/utils';
import type { BlockModel } from '@blocksuite/store';
@@ -200,15 +200,6 @@ export async function resetImageSize(
});
}
function convertToString(blob: Blob): Promise<string | null> {
return new Promise(resolve => {
const reader = new FileReader();
reader.addEventListener('load', _ => resolve(reader.result as string));
reader.addEventListener('error', () => resolve(null));
reader.readAsDataURL(blob);
});
}
function convertToPng(blob: Blob): Promise<Blob | null> {
return new Promise(resolve => {
const reader = new FileReader();
@@ -234,33 +225,35 @@ function convertToPng(blob: Blob): Promise<Blob | null> {
export async function copyImageBlob(
block: ImageBlockComponent | ImageEdgelessBlockComponent
) {
const { host, model } = block;
const { host, model, std } = block;
let blob = await getImageBlob(model);
if (!blob) {
console.error('Failed to get image blob');
return;
}
let copied = false;
try {
// @ts-expect-error FIXME: BS-2239
if (window.apis?.clipboard?.copyAsImageFromString) {
const dataURL = await convertToString(blob);
if (!dataURL)
throw new BlockSuiteError(
ErrorCode.DefaultRuntimeError,
'Cant convert a blob to data URL.'
);
// @ts-expect-error FIXME: BS-2239
await window.apis.clipboard?.copyAsImageFromString(dataURL);
} else {
// DOMException: Type image/jpeg not supported on write.
// Copies the image as PNG in Electron.
const copyAsPNG = std.getOptional(NativeClipboardProvider)?.copyAsPNG;
if (copyAsPNG) {
copied = await copyAsPNG(await blob.arrayBuffer());
}
// The current clipboard only supports the `image/png` image format.
// The `ClipboardItem.supports('image/svg+xml')` is not currently used,
// because when pasting, the content is not read correctly.
//
// https://developer.mozilla.org/en-US/docs/Web/API/ClipboardItem
// https://alexharri.com/blog/clipboard
if (!copied) {
if (blob.type !== 'image/png') {
const pngBlob = await convertToPng(blob);
if (!pngBlob) {
blob = await convertToPng(blob);
if (!blob) {
console.error('Failed to convert blob to PNG');
return;
}
blob = pngBlob;
}
if (!globalThis.isSecureContext) {