From 052974a3086b5df050bfd0b1f2364e333cd3daf9 Mon Sep 17 00:00:00 2001 From: 3ae3ae Date: Tue, 29 Sep 2026 15:43:16 +0900 Subject: [PATCH] fix(app): preserve text when the clipboard also contains an image --- .../e2e/browser/composer-attachments.spec.ts | 32 +++++++++++++++++++ packages/app/src/composer/input/input.tsx | 1 + 2 files changed, 33 insertions(+) diff --git a/packages/app/e2e/browser/composer-attachments.spec.ts b/packages/app/e2e/browser/composer-attachments.spec.ts index 94a52ace16..7d24ebc0ce 100644 --- a/packages/app/e2e/browser/composer-attachments.spec.ts +++ b/packages/app/e2e/browser/composer-attachments.spec.ts @@ -4,6 +4,7 @@ import { expectComposerVisible } from "../support/helpers/composer"; import { expectAgentIdle } from "../support/helpers/agent-stream"; import { openAttachmentMenu, + composerLocator, expectAttachmentSheetRowsOnTitleRail, openGithubPickerFromMenu, attachImageFromMenu, @@ -53,6 +54,37 @@ const TEST_JSON = { }; test.describe("Composer attachments", () => { + test("pastes copied document text instead of its image representation", async ({ + page, + context, + withWorkspace, + }) => { + const workspace = await withWorkspace({ prefix: "paste-document-text-" }); + await workspace.navigateTo(); + await clickNewChat(page); + await expectComposerVisible(page); + await context.grantPermissions(["clipboard-read", "clipboard-write"]); + const text = "Document text with 한글 and two lines.\nSecond line."; + await page.evaluate( + async ({ content, png }) => { + const response = await fetch(`data:image/png;base64,${png}`); + const image = await response.blob(); + await navigator.clipboard.write([ + new ClipboardItem({ + "text/plain": new Blob([content], { type: "text/plain" }), + "text/html": new Blob([`

${content}

`], { type: "text/html" }), + "image/png": image, + }), + ]); + }, + { content: text, png: MINIMAL_PNG.toString("base64") }, + ); + await composerLocator(page).click(); + await page.keyboard.press("ControlOrMeta+v"); + await expectComposerDraft(page, text); + await expect(page.getByTestId("composer-image-attachment-pill")).toHaveCount(0); + }); + test("selected file shows a loading attachment until upload is acknowledged", async ({ page, withWorkspace, diff --git a/packages/app/src/composer/input/input.tsx b/packages/app/src/composer/input/input.tsx index 0300dd518c..af4c4c5b74 100644 --- a/packages/app/src/composer/input/input.tsx +++ b/packages/app/src/composer/input/input.tsx @@ -471,6 +471,7 @@ function usePasteImagesEffect(args: PasteImagesEffectArgs): void { let disposed = false; const handlePaste = (event: ClipboardEvent) => { if (!isConnected || disabled || isDictating || isRealtimeVoiceForCurrentAgent) return; + if (event.clipboardData?.getData("text/plain")) return; const imageFiles = collectImageFilesFromClipboardData(event.clipboardData); if (imageFiles.length === 0) return;