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:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user