diff --git a/README.md b/README.md index 97fe5e8..e31a766 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ message meets a specified criterion. - **Automatic rules** – create rules that tag or move new messages based on AI classification. - **Rule ordering** – drag rules to prioritize them and optionally stop processing after a match. - **Context menu** – apply AI rules from the message list or the message display action button. -- **Status icons** – toolbar icons indicate when messages are queued or being classified. +- **Status icons** – toolbar icons show when classification is in progress and briefly display success or error states. - **Packaging script** – `build-xpi.ps1` builds an XPI ready for installation. ## Architecture Overview diff --git a/background.js b/background.js index 1463ee7..e47bf99 100644 --- a/background.js +++ b/background.js @@ -18,14 +18,9 @@ let aiRules = []; let queue = Promise.resolve(); let queuedCount = 0; let processing = false; +let iconTimer = null; -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"; - } +function setIcon(path) { if (browser.browserAction) { browser.browserAction.setIcon({ path }); } @@ -34,6 +29,20 @@ function updateActionIcon() { } } +function updateActionIcon() { + let path = "resources/img/logo32.png"; + if (processing || queuedCount > 0) { + path = "resources/img/busy.png"; + } + setIcon(path); +} + +function showTransientIcon(path, delay = 1500) { + clearTimeout(iconTimer); + setIcon(path); + iconTimer = setTimeout(updateActionIcon, delay); +} + async function sha256Hex(str) { const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(str)); return Array.from(new Uint8Array(buf), b => b.toString(16).padStart(2, '0')).join(''); @@ -125,11 +134,12 @@ async function applyAiRules(idsInput) { } } } - } catch (e) { - logger.aiLog("failed to apply AI rules", { level: 'error' }, e); - } finally { processing = false; - updateActionIcon(); + showTransientIcon("resources/img/done.png"); + } catch (e) { + processing = false; + logger.aiLog("failed to apply AI rules", { level: 'error' }, e); + showTransientIcon("resources/img/error.png"); } }); }