From 055a5fd6046bf0edb7cb56f3cff9d965024b9410 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 4 Jun 2026 19:29:45 +0000 Subject: [PATCH] fix(attachments): use downloads API instead of a.click() in popup The programmatic anchor-click approach caused Firefox to close the popup (focus loss during download initiation), making attachment downloads look like they did nothing. Replace with api.downloads.download() which is the correct extension API for initiating downloads from a popup context. Also surfaces download errors visually (red border on the button) and logs them to the console, so failures are no longer completely silent. Adds the "downloads" manifest permission required by the API. https://claude.ai/code/session_011k8zuM3FhFRooN1fpGYhNi --- src/manifest.json | 3 ++- src/popup/actions.js | 16 +++++++++------- src/popup/detail.js | 6 ++++-- src/popup/popup.css | 5 +++++ 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/manifest.json b/src/manifest.json index 6a45a3c..6f2e429 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -22,7 +22,8 @@ "storage", "alarms", "notifications", - "contextMenus" + "contextMenus", + "downloads" ], "host_permissions": [ "https://www.googleapis.com/*", diff --git a/src/popup/actions.js b/src/popup/actions.js index 68dee33..73532f3 100644 --- a/src/popup/actions.js +++ b/src/popup/actions.js @@ -81,13 +81,15 @@ export async function downloadAttachment(accountId, messageId, attachment) { } const blob = new Blob([bytes], { type: attachment.mimeType || 'application/octet-stream' }); const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = attachment.filename || 'attachment'; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - setTimeout(() => URL.revokeObjectURL(url), 1000); + try { + await api.downloads.download({ + url, + filename: attachment.filename || 'attachment', + saveAs: false, + }); + } finally { + setTimeout(() => URL.revokeObjectURL(url), 5000); + } } export async function bulkAction(action) { diff --git a/src/popup/detail.js b/src/popup/detail.js index ae65148..72db713 100644 --- a/src/popup/detail.js +++ b/src/popup/detail.js @@ -59,10 +59,12 @@ function buildAttachmentItem(accountId, messageId, att) { item.addEventListener('click', async () => { item.disabled = true; + item.classList.remove('attachment-item--error'); try { await downloadAttachment(accountId, messageId, att); - } catch { - // Silently fail — the file icon just stays enabled on next click. + } catch (err) { + console.error('Attachment download failed:', err); + item.classList.add('attachment-item--error'); } finally { item.disabled = false; } diff --git a/src/popup/popup.css b/src/popup/popup.css index 5cfced3..2050a2c 100644 --- a/src/popup/popup.css +++ b/src/popup/popup.css @@ -699,6 +699,11 @@ body { cursor: wait; } +.attachment-item--error { + border-color: var(--danger); + color: var(--danger); +} + .attachment-icon { color: var(--text-secondary); flex-shrink: 0;