diff --git a/ui/src/e2e/new-session-page.prompt-attachments.e2e.test.ts b/ui/src/e2e/new-session-page.prompt-attachments.e2e.test.ts index 24bf0e0fbb8e..d843ef142558 100644 --- a/ui/src/e2e/new-session-page.prompt-attachments.e2e.test.ts +++ b/ui/src/e2e/new-session-page.prompt-attachments.e2e.test.ts @@ -143,6 +143,29 @@ suite.define(() => { }); }); + it("previews and removes a picked image without object URL support", async () => { + await withNewSessionPage(async (page) => { + await page.addInitScript(() => { + Object.defineProperty(URL, "createObjectURL", { configurable: true, value: undefined }); + }); + await installMockGateway(page); + await page.goto(`${suite.server.baseUrl}new`); + + await page + .locator(".agent-chat__photo-input") + .setInputFiles(path.join(process.cwd(), "ui/public/favicon-32.png")); + + const attachment = page.locator(".chat-attachment-thumb"); + const preview = attachment.locator('img[alt="Attachment preview"]'); + await preview.waitFor({ state: "visible" }); + await expect.poll(() => preview.getAttribute("src")).toMatch(/^data:image\/png;base64,/u); + await captureUiProof(page, "new-session-picked-image-preview.png"); + await page.getByRole("button", { name: "Remove attachment" }).click(); + await expect.poll(() => attachment.count()).toBe(0); + await captureUiProof(page, "new-session-picked-image-removed.png"); + }); + }); + it("shows the initial prompt while the newly created session is still running", async () => { await withNewSessionPage(async (page) => { const sessionKey = "agent:main:visible-initial-prompt"; diff --git a/ui/src/pages/chat/attachment-payload-store.ts b/ui/src/pages/chat/attachment-payload-store.ts index 5f93d8af0e78..c370971bf8e5 100644 --- a/ui/src/pages/chat/attachment-payload-store.ts +++ b/ui/src/pages/chat/attachment-payload-store.ts @@ -1,4 +1,3 @@ -// Control UI chat module implements attachment payload store behavior. import type { ChatAttachment } from "../../lib/chat/chat-types.ts"; type AttachmentPayload = { @@ -45,10 +44,10 @@ export function getChatAttachmentDataUrl(attachment: ChatAttachment): string | n return attachment.dataUrl ?? payloads.get(attachment.id)?.dataUrl ?? null; } +// Stored data URLs keep previews available when this browser cannot create object URLs. export function getChatAttachmentPreviewUrl(attachment: ChatAttachment): string | null { - return ( - attachment.previewUrl ?? payloads.get(attachment.id)?.previewUrl ?? attachment.dataUrl ?? null - ); + const storedPreview = payloads.get(attachment.id)?.previewUrl; + return attachment.previewUrl ?? storedPreview ?? getChatAttachmentDataUrl(attachment); } function cloneChatAttachmentMetadata(attachment: ChatAttachment): ChatAttachment {