From 097a63362c4f2fe8e6aef1b42c8727005eaab9ff Mon Sep 17 00:00:00 2001 From: Aadi <123532141+golok727@users.noreply.github.com> Date: Thu, 12 Jun 2025 13:22:14 +0530 Subject: [PATCH] fix(editor): firefox can't paste image in edgeless (#12729) fix: https://github.com/toeverything/blocksuite/issues/8718 ## Summary by CodeRabbit ## Summary by CodeRabbit - **Bug Fixes** - Improved clipboard handling to prevent creating empty notes when pasting blank text. - Enhanced detection of files and SVG images in clipboard content for more reliable pasting behavior. --- .../root/src/edgeless/clipboard/clipboard.ts | 4 ++ .../root/src/edgeless/clipboard/utils.ts | 52 ++++++++++--------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/blocksuite/affine/blocks/root/src/edgeless/clipboard/clipboard.ts b/blocksuite/affine/blocks/root/src/edgeless/clipboard/clipboard.ts index 5f33f9b27..eb817190b 100644 --- a/blocksuite/affine/blocks/root/src/edgeless/clipboard/clipboard.ts +++ b/blocksuite/affine/blocks/root/src/edgeless/clipboard/clipboard.ts @@ -606,6 +606,10 @@ export class EdgelessClipboardController extends PageClipboard { } private async _pasteTextContentAsNote(content: BlockSnapshot[] | string) { + if (content === '') { + return; + } + const { x, y } = this.toolManager.lastMousePos$.peek(); const noteProps = { diff --git a/blocksuite/affine/blocks/root/src/edgeless/clipboard/utils.ts b/blocksuite/affine/blocks/root/src/edgeless/clipboard/utils.ts index 6dcfb565e..009a1cf8e 100644 --- a/blocksuite/affine/blocks/root/src/edgeless/clipboard/utils.ts +++ b/blocksuite/affine/blocks/root/src/edgeless/clipboard/utils.ts @@ -69,37 +69,39 @@ export async function prepareClipboardData( export function isPureFileInClipboard(clipboardData: DataTransfer) { const types = clipboardData.types; - return ( - (types.length === 1 && types[0] === 'Files') || - (types.length === 2 && - (types.includes('text/plain') || types.includes('text/html')) && - types.includes('Files')) - ); + const allowedTypes = new Set([ + 'Files', + 'text/plain', + 'text/html', + 'application/x-moz-file', + ]); + + return types.includes('Files') && types.every(type => allowedTypes.has(type)); } export function tryGetSvgFromClipboard(clipboardData: DataTransfer) { - const types = clipboardData.types; + try { + const parser = new DOMParser(); + const svgDoc = parser.parseFromString( + clipboardData.getData('text/plain'), + 'image/svg+xml' + ); + const svg = svgDoc.documentElement; - if (types.length === 1 && types[0] !== 'text/plain') { + if (svg.tagName !== 'svg' || !svg.hasAttribute('xmlns')) { + return null; + } + const svgContent = DOMPurify.sanitize(svgDoc.documentElement, { + USE_PROFILES: { svg: true }, + }); + const blob = new Blob([svgContent], { type: 'image/svg+xml' }); + const file = new File([blob], 'pasted-image.svg', { + type: 'image/svg+xml', + }); + return file; + } catch { return null; } - - const parser = new DOMParser(); - const svgDoc = parser.parseFromString( - clipboardData.getData('text/plain'), - 'image/svg+xml' - ); - const svg = svgDoc.documentElement; - - if (svg.tagName !== 'svg' || !svg.hasAttribute('xmlns')) { - return null; - } - const svgContent = DOMPurify.sanitize(svgDoc.documentElement, { - USE_PROFILES: { svg: true }, - }); - const blob = new Blob([svgContent], { type: 'image/svg+xml' }); - const file = new File([blob], 'pasted-image.svg', { type: 'image/svg+xml' }); - return file; } export function edgelessElementsBoundFromRawData(