diff --git a/blocksuite/framework/std/src/clipboard/clipboard.ts b/blocksuite/framework/std/src/clipboard/clipboard.ts index 8661fe949b..1dc789dd17 100644 --- a/blocksuite/framework/std/src/clipboard/clipboard.ts +++ b/blocksuite/framework/std/src/clipboard/clipboard.ts @@ -76,6 +76,7 @@ export class Clipboard extends LifeCycleWatcher { const byPriority = Array.from(this._adapters).sort( (a, b) => b.priority - a.priority ); + for (const { adapter, mimeType } of byPriority) { const item = getItem(mimeType); if (Array.isArray(item)) { @@ -170,7 +171,9 @@ export class Clipboard extends LifeCycleWatcher { index?: number ) => { const data = event.clipboardData; - if (!data) return; + if (!data) { + return; + } try { const json = this.readFromClipboard(data); @@ -187,7 +190,7 @@ export class Clipboard extends LifeCycleWatcher { ); } return slice; - } catch { + } catch (error) { const getDataByType = this._getDataByType(data); const slice = await this._getSnapshotByPriority( type => getDataByType(type), @@ -195,7 +198,6 @@ export class Clipboard extends LifeCycleWatcher { parent, index ); - return slice; } }; @@ -292,9 +294,7 @@ export class Clipboard extends LifeCycleWatcher { if (image) { const type = 'image/png'; - delete items[type]; - if (typeof image === 'string') { clipboardItems[type] = new Blob([image], { type }); } else if (image instanceof Blob) { @@ -314,7 +314,7 @@ export class Clipboard extends LifeCycleWatcher { if (hasInnerHTML || isEmpty) { const type = 'text/html'; const snapshot = lz.compressToEncodedURIComponent(JSON.stringify(items)); - const html = `
${innerHTML}
`; + const html = `
${innerHTML}
`; clipboardItems[type] = new Blob([html], { type }); } diff --git a/blocksuite/framework/std/src/event/control/clipboard.ts b/blocksuite/framework/std/src/event/control/clipboard.ts index 2993a61178..be681a5f3c 100644 --- a/blocksuite/framework/std/src/event/control/clipboard.ts +++ b/blocksuite/framework/std/src/event/control/clipboard.ts @@ -28,7 +28,6 @@ export class ClipboardControl { const clipboardEventState = new ClipboardEventState({ event, }); - this._dispatcher.run( 'paste', this._createContext(event, clipboardEventState) diff --git a/tests/affine-local/e2e/blocksuite/clipboard/clipboard.spec.ts b/tests/affine-local/e2e/blocksuite/clipboard/clipboard.spec.ts index e4aaae7563..d4b65c643a 100644 --- a/tests/affine-local/e2e/blocksuite/clipboard/clipboard.spec.ts +++ b/tests/affine-local/e2e/blocksuite/clipboard/clipboard.spec.ts @@ -474,6 +474,79 @@ test.describe('paste in readonly mode', () => { }); }); +test.describe('cross document clipboard regression', () => { + test('copy and paste paragraph content between docs', async ({ page }) => { + const container = locateEditorContainer(page); + await container.click(); + + const sourceText = "Cross-doc paste can't fail again"; + await type(page, sourceText); + + const { blockIds } = await getParagraphIds(page); + await setSelection(page, blockIds[0], 0, blockIds[0], sourceText.length); + + await copyByKeyboard(page); + + await clickNewPageButton(page, 'Clipboard Destination'); + await waitForEditorLoad(page); + + const destination = locateEditorContainer(page); + await destination.click(); + + await pasteByKeyboard(page); + await page.waitForTimeout(100); + + const pastedTexts = await page.locator(paragraphLocator).allTextContents(); + expect(pastedTexts.some(text => text.includes(sourceText))).toBe(true); + }); + + test('copied content remains available to external clipboard consumers', async ({ + page, + }) => { + const container = locateEditorContainer(page); + await container.click(); + + const textForExternal = 'External clipboard visibility check'; + await type(page, textForExternal); + + const { blockIds } = await getParagraphIds(page); + await setSelection( + page, + blockIds[0], + 0, + blockIds[0], + textForExternal.length + ); + + await copyByKeyboard(page); + + const plainText = await page.evaluate(() => navigator.clipboard.readText()); + + expect(plainText).toBe(textForExternal); + }); + + test('copy and paste within a single document still duplicates content', async ({ + page, + }) => { + const container = locateEditorContainer(page); + await container.click(); + + const intraDocText = 'Same doc paste regression guard'; + await type(page, intraDocText); + + const { blockIds } = await getParagraphIds(page); + await setSelection(page, blockIds[0], 0, blockIds[0], intraDocText.length); + + await copyByKeyboard(page); + + await pressEnter(page); + await pasteByKeyboard(page); + await page.waitForTimeout(100); + + await verifyParagraphContent(page, 1, intraDocText); + }); +}); + test('should copy single image from edgeless and paste to page', async ({ page, }) => {