Remove placeholder status icons

This commit is contained in:
Jordan Wages 2025-06-25 14:53:19 -05:00
commit a58b8f3632
3 changed files with 55 additions and 18 deletions

View file

@ -21,6 +21,7 @@ message meets a specified criterion.
- **Debug logging** optional colorized logs help troubleshoot interactions with the AI service. - **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. - **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. - **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. - **Packaging script** `build-xpi.ps1` builds an XPI ready for installation.
## Architecture Overview ## Architecture Overview

View file

@ -13,6 +13,24 @@
let logger; let logger;
let AiClassifier; let AiClassifier;
let aiRules = []; 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) { async function sha256Hex(str) {
const buf = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(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) { async function applyAiRules(idsInput) {
const ids = Array.isArray(idsInput) ? idsInput : [idsInput]; const ids = Array.isArray(idsInput) ? idsInput : [idsInput];
if (!ids.length) return; if (!ids.length) return queue;
if (!aiRules.length) { if (!aiRules.length) {
const { aiRules: stored } = await browser.storage.local.get("aiRules"); const { aiRules: stored } = await browser.storage.local.get("aiRules");
@ -36,28 +54,40 @@ async function applyAiRules(idsInput) {
for (const msg of ids) { for (const msg of ids) {
const id = msg?.id ?? msg; const id = msg?.id ?? msg;
try { queuedCount++;
const full = await messenger.messages.getFull(id); updateActionIcon();
const text = full?.parts?.[0]?.body || ""; queue = queue.then(async () => {
for (const rule of aiRules) { processing = true;
const cacheKey = await sha256Hex(`${id}|${rule.criterion}`); queuedCount--;
const matched = await AiClassifier.classifyText(text, rule.criterion, cacheKey); updateActionIcon();
if (matched) { try {
for (const act of (rule.actions || [])) { const full = await messenger.messages.getFull(id);
if (act.type === 'tag' && act.tagKey) { const text = full?.parts?.[0]?.body || "";
await messenger.messages.update(id, { tags: [act.tagKey] }); for (const rule of aiRules) {
} else if (act.type === 'move' && act.folder) { const cacheKey = await sha256Hex(`${id}|${rule.criterion}`);
await messenger.messages.move([id], act.folder); const matched = await AiClassifier.classifyText(text, rule.criterion, cacheKey);
} else if (act.type === 'junk') { if (matched) {
await messenger.messages.update(id, { junk: !!act.junk }); 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 () => { (async () => {

View file

@ -18,6 +18,12 @@
"96": "resources/img/logo96.png", "96": "resources/img/logo96.png",
"128": "resources/img/logo128.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" ] }, "background": { "scripts": [ "background.js" ] },
"options_ui": { "options_ui": {
"page": "options/options.html", "page": "options/options.html",