From a58b8f3632866addcdc02f2c6125e6f40263f6b8 Mon Sep 17 00:00:00 2001 From: Jordan Wages Date: Wed, 25 Jun 2025 14:53:19 -0500 Subject: [PATCH] Remove placeholder status icons --- README.md | 1 + background.js | 66 +++++++++++++++++++++++++++++++++++++-------------- manifest.json | 6 +++++ 3 files changed, 55 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 49d436a..f07ee24 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ message meets a specified criterion. - **Debug logging** – optional colorized logs help troubleshoot interactions with the AI service. - **Automatic rules** – create rules that tag or move new messages based on AI classification. - **Context menu** – apply AI rules to selected messages from the message list or display. +- **Status icons** – toolbar icons indicate when messages are queued or being classified. - **Packaging script** – `build-xpi.ps1` builds an XPI ready for installation. ## Architecture Overview diff --git a/background.js b/background.js index 3ffa635..57ee75b 100644 --- a/background.js +++ b/background.js @@ -13,6 +13,24 @@ let logger; let AiClassifier; let aiRules = []; +let queue = Promise.resolve(); +let queuedCount = 0; +let processing = false; + +function updateActionIcon() { + let path = "resources/img/logo32.png"; + if (processing) { + path = "resources/img/status-classifying.png"; + } else if (queuedCount > 0) { + path = "resources/img/status-queued.png"; + } + if (browser.browserAction) { + browser.browserAction.setIcon({ path }); + } + if (browser.messageDisplayAction) { + browser.messageDisplayAction.setIcon({ path }); + } +} async function sha256Hex(str) { const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(str)); @@ -21,7 +39,7 @@ async function sha256Hex(str) { async function applyAiRules(idsInput) { const ids = Array.isArray(idsInput) ? idsInput : [idsInput]; - if (!ids.length) return; + if (!ids.length) return queue; if (!aiRules.length) { const { aiRules: stored } = await browser.storage.local.get("aiRules"); @@ -36,28 +54,40 @@ async function applyAiRules(idsInput) { for (const msg of ids) { const id = msg?.id ?? msg; - try { - const full = await messenger.messages.getFull(id); - const text = full?.parts?.[0]?.body || ""; - for (const rule of aiRules) { - const cacheKey = await sha256Hex(`${id}|${rule.criterion}`); - const matched = await AiClassifier.classifyText(text, rule.criterion, cacheKey); - if (matched) { - for (const act of (rule.actions || [])) { - if (act.type === 'tag' && act.tagKey) { - await messenger.messages.update(id, { tags: [act.tagKey] }); - } else if (act.type === 'move' && act.folder) { - await messenger.messages.move([id], act.folder); - } else if (act.type === 'junk') { - await messenger.messages.update(id, { junk: !!act.junk }); + queuedCount++; + updateActionIcon(); + queue = queue.then(async () => { + processing = true; + queuedCount--; + updateActionIcon(); + try { + const full = await messenger.messages.getFull(id); + const text = full?.parts?.[0]?.body || ""; + for (const rule of aiRules) { + const cacheKey = await sha256Hex(`${id}|${rule.criterion}`); + const matched = await AiClassifier.classifyText(text, rule.criterion, cacheKey); + if (matched) { + for (const act of (rule.actions || [])) { + if (act.type === 'tag' && act.tagKey) { + await messenger.messages.update(id, { tags: [act.tagKey] }); + } else if (act.type === 'move' && act.folder) { + await messenger.messages.move([id], act.folder); + } else if (act.type === 'junk') { + await messenger.messages.update(id, { junk: !!act.junk }); + } } } } + } catch (e) { + logger.aiLog("failed to apply AI rules", { level: 'error' }, e); + } finally { + processing = false; + updateActionIcon(); } - } catch (e) { - logger.aiLog("failed to apply AI rules", { level: 'error' }, e); - } + }); } + + return queue; } (async () => { diff --git a/manifest.json b/manifest.json index cb30d19..1245a83 100644 --- a/manifest.json +++ b/manifest.json @@ -18,6 +18,12 @@ "96": "resources/img/logo96.png", "128": "resources/img/logo128.png" }, + "browser_action": { + "default_icon": "resources/img/logo32.png" + }, + "message_display_action": { + "default_icon": "resources/img/logo32.png" + }, "background": { "scripts": [ "background.js" ] }, "options_ui": { "page": "options/options.html",