fix(editor): firefox can't paste image in edgeless (#12729)

fix: https://github.com/toeverything/blocksuite/issues/8718
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## 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.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Aadi
2025-06-12 13:22:14 +05:30
committed by GitHub
parent 7284320355
commit 097a63362c
2 changed files with 31 additions and 25 deletions
@@ -606,6 +606,10 @@ export class EdgelessClipboardController extends PageClipboard {
} }
private async _pasteTextContentAsNote(content: BlockSnapshot[] | string) { private async _pasteTextContentAsNote(content: BlockSnapshot[] | string) {
if (content === '') {
return;
}
const { x, y } = this.toolManager.lastMousePos$.peek(); const { x, y } = this.toolManager.lastMousePos$.peek();
const noteProps = { const noteProps = {
@@ -69,37 +69,39 @@ export async function prepareClipboardData(
export function isPureFileInClipboard(clipboardData: DataTransfer) { export function isPureFileInClipboard(clipboardData: DataTransfer) {
const types = clipboardData.types; const types = clipboardData.types;
return ( const allowedTypes = new Set([
(types.length === 1 && types[0] === 'Files') || 'Files',
(types.length === 2 && 'text/plain',
(types.includes('text/plain') || types.includes('text/html')) && 'text/html',
types.includes('Files')) 'application/x-moz-file',
); ]);
return types.includes('Files') && types.every(type => allowedTypes.has(type));
} }
export function tryGetSvgFromClipboard(clipboardData: DataTransfer) { 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; 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( export function edgelessElementsBoundFromRawData(