diff --git a/ai-filter.sln b/ai-filter.sln index 77a10cb..960bee0 100644 --- a/ai-filter.sln +++ b/ai-filter.sln @@ -13,8 +13,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution logger.js = logger.js manifest.json = manifest.json README.md = README.md - reasoning.html = reasoning.html - reasoning.js = reasoning.js EndProjectSection ProjectSection(FolderGlobals) = preProject Q_5_4Users_4Jordan_4Documents_4Gitea_4thunderbird-ai-filter_4src_4manifest_1json__JsonSchema = @@ -54,8 +52,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "prompt_templates", "prompt_ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "resources", "resources", "{68A87938-5C2B-49F5-8AAA-8A34FBBFD854}" ProjectSection(SolutionItems) = preProject - resources\clearCacheButton.js = resources\clearCacheButton.js - resources\reasonButton.js = resources\reasonButton.js EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "img", "img", "{F266602F-1755-4A95-A11B-6C90C701C5BF}" diff --git a/background.js b/background.js index 1b25492..8e235d4 100644 --- a/background.js +++ b/background.js @@ -30,7 +30,7 @@ function setIcon(path) { } function updateActionIcon() { - let path = "resources/img/logo32.png"; + let path = "resources/img/brain.png"; if (processing || queuedCount > 0) { path = "resources/img/busy.png"; } @@ -223,40 +223,9 @@ async function clearCacheForMessages(idsInput) { logger.aiLog("background.js loaded – ready to classify", {debug: true}); if (browser.messageDisplayAction) { - browser.messageDisplayAction.setTitle({ title: "Classify" }); + browser.messageDisplayAction.setTitle({ title: "Details" }); if (browser.messageDisplayAction.setLabel) { - browser.messageDisplayAction.setLabel({ label: "Classify" }); - } - } - if (browser.scripting && browser.scripting.messageDisplay) { - try { - const scripts = [ - { - id: "clear-cache-button", - js: ["resources/clearCacheButton.js"], - }, - { - id: "reason-button", - js: ["resources/reasonButton.js"], - }, - ]; - await browser.scripting.messageDisplay.registerScripts(scripts); - } catch (e) { - logger.aiLog("failed to register message display script", { level: 'warn' }, e); - } - } else if (browser.messageDisplayScripts) { - try { - const scripts = [ - { js: ["resources/clearCacheButton.js"] }, - { js: ["resources/reasonButton.js"] }, - ]; - if (browser.messageDisplayScripts.registerScripts) { - await browser.messageDisplayScripts.registerScripts(scripts); - } else if (browser.messageDisplayScripts.register) { - await browser.messageDisplayScripts.register(scripts); - } - } catch (e) { - logger.aiLog("failed to register message display script", { level: 'warn' }, e); + browser.messageDisplayAction.setLabel({ label: "Details" }); } } @@ -293,17 +262,7 @@ async function clearCacheForMessages(idsInput) { icons: { "16": "resources/img/brain.png" } }); - if (browser.messageDisplayAction) { - browser.messageDisplayAction.onClicked.addListener(async (tab) => { - try { - const msgs = await browser.messageDisplay.getDisplayedMessages(tab.id); - const ids = msgs.map(m => m.id); - await applyAiRules(ids); - } catch (e) { - logger.aiLog("failed to apply AI rules from action", { level: 'error' }, e); - } - }); - } + browser.menus.onClicked.addListener(async info => { if (info.menuItemId === "apply-ai-rules-list" || info.menuItemId === "apply-ai-rules-display") { @@ -317,7 +276,7 @@ async function clearCacheForMessages(idsInput) { } else if (info.menuItemId === "view-ai-reason-list" || info.menuItemId === "view-ai-reason-display") { const id = info.messageId || info.selectedMessages?.messages?.[0]?.id; if (id) { - const url = browser.runtime.getURL(`reasoning.html?mid=${id}`); + const url = browser.runtime.getURL(`details.html?mid=${id}`); browser.tabs.create({ url }); } } @@ -383,6 +342,45 @@ async function clearCacheForMessages(idsInput) { logger.aiLog("failed to collect reasons", { level: 'error' }, e); return { subject: '', reasons: [] }; } + } else if (msg?.type === "sortana:getDetails") { + try { + const id = msg.id; + const hdr = await messenger.messages.get(id); + const subject = hdr?.subject || ""; + if (!aiRules.length) { + const { aiRules: stored } = await storage.local.get("aiRules"); + aiRules = Array.isArray(stored) ? stored.map(r => { + if (r.actions) return r; + const actions = []; + if (r.tag) actions.push({ type: 'tag', tagKey: r.tag }); + if (r.moveTo) actions.push({ type: 'move', folder: r.moveTo }); + const rule = { criterion: r.criterion, actions }; + if (r.stopProcessing) rule.stopProcessing = true; + return rule; + }) : []; + } + const results = []; + for (const rule of aiRules) { + const key = await sha256Hex(`${id}|${rule.criterion}`); + const matched = AiClassifier.getCachedResult(key); + const reason = AiClassifier.getReason(key); + if (matched !== null || reason) { + results.push({ criterion: rule.criterion, matched, reason }); + } + } + return { subject, results }; + } catch (e) { + logger.aiLog("failed to collect details", { level: 'error' }, e); + return { subject: '', results: [] }; + } + } else if (msg?.type === "sortana:clearCacheForMessage") { + try { + await clearCacheForMessages([msg.id]); + return { ok: true }; + } catch (e) { + logger.aiLog("failed to clear cache for message", { level: 'error' }, e); + return { ok: false }; + } } else { logger.aiLog("Unknown message type, ignoring", {level: 'warn'}, msg?.type); } diff --git a/reasoning.html b/details.html similarity index 61% rename from reasoning.html rename to details.html index bb1577d..1502471 100644 --- a/reasoning.html +++ b/details.html @@ -2,7 +2,7 @@ - AI Reasoning + AI Details @@ -10,8 +10,11 @@

+
+ +
- + diff --git a/reasoning.js b/details.js similarity index 54% rename from reasoning.js rename to details.js index eb0b9da..2b5b9e7 100644 --- a/reasoning.js +++ b/details.js @@ -3,25 +3,33 @@ document.addEventListener('DOMContentLoaded', async () => { const id = parseInt(params.get('mid'), 10); if (!id) return; try { - const { subject, reasons } = await browser.runtime.sendMessage({ type: 'sortana:getReasons', id }); + const { subject, results } = await browser.runtime.sendMessage({ type: 'sortana:getDetails', id }); document.getElementById('subject').textContent = subject; const container = document.getElementById('rules'); - for (const r of reasons) { + for (const r of results) { const article = document.createElement('article'); - article.className = 'message mb-4'; + const color = r.matched === true ? 'is-success' : 'is-danger'; + article.className = `message ${color} mb-4`; const header = document.createElement('div'); header.className = 'message-header'; header.innerHTML = `

${r.criterion}

`; const body = document.createElement('div'); body.className = 'message-body'; + const status = document.createElement('p'); + status.textContent = r.matched ? 'Matched' : 'Did not match'; const pre = document.createElement('pre'); - pre.textContent = r.reason; + pre.textContent = r.reason || ''; + body.appendChild(status); body.appendChild(pre); article.appendChild(header); article.appendChild(body); container.appendChild(article); } + document.getElementById('clear').addEventListener('click', async () => { + await browser.runtime.sendMessage({ type: 'sortana:clearCacheForMessage', id }); + window.close(); + }); } catch (e) { - console.error('failed to load reasons', e); + console.error('failed to load details', e); } }); diff --git a/manifest.json b/manifest.json index 3c0b02b..3f17845 100644 --- a/manifest.json +++ b/manifest.json @@ -22,9 +22,10 @@ "default_icon": "resources/img/logo32.png" }, "message_display_action": { - "default_icon": "resources/img/logo32.png", - "default_title": "Classify", - "default_label": "Classify" + "default_icon": "resources/img/brain.png", + "default_title": "Details", + "default_label": "Details", + "default_popup": "details.html" }, "background": { "scripts": [ "background.js" ] }, "options_ui": { diff --git a/resources/clearCacheButton.js b/resources/clearCacheButton.js deleted file mode 100644 index a4d2adb..0000000 --- a/resources/clearCacheButton.js +++ /dev/null @@ -1,22 +0,0 @@ -(function() { - function addButton() { - const toolbar = document.querySelector("#header-view-toolbar") || - document.querySelector("#mail-toolbox toolbar"); - if (!toolbar || document.getElementById('sortana-clear-cache-button')) return; - const button = document.createXULElement ? - document.createXULElement('toolbarbutton') : - document.createElement('button'); - button.id = 'sortana-clear-cache-button'; - button.setAttribute('label', 'Clear Cache'); - button.className = 'toolbarbutton-1'; - button.addEventListener('command', () => { - browser.runtime.sendMessage({ type: 'sortana:clearCacheForDisplayed' }); - }); - toolbar.appendChild(button); - } - if (document.readyState === 'complete' || document.readyState === 'interactive') { - addButton(); - } else { - document.addEventListener('DOMContentLoaded', addButton, { once: true }); - } -})(); diff --git a/resources/reasonButton.js b/resources/reasonButton.js deleted file mode 100644 index d0b38e7..0000000 --- a/resources/reasonButton.js +++ /dev/null @@ -1,34 +0,0 @@ -(function() { - function addButton() { - const toolbar = document.querySelector("#header-view-toolbar") || - document.querySelector("#mail-toolbox toolbar"); - if (!toolbar || document.getElementById('sortana-reason-button')) return; - const button = document.createXULElement ? - document.createXULElement('toolbarbutton') : - document.createElement('button'); - button.id = 'sortana-reason-button'; - button.setAttribute('label', 'View Reasoning'); - button.className = 'toolbarbutton-1'; - const icon = browser.runtime.getURL('resources/img/brain.png'); - if (button.setAttribute) { - button.setAttribute('image', icon); - } else { - button.style.backgroundImage = `url(${icon})`; - button.style.backgroundSize = 'contain'; - } - button.addEventListener('command', async () => { - const tabs = await browser.tabs.query({ active: true, currentWindow: true }); - const tabId = tabs[0]?.id; - const msgs = tabId ? await browser.messageDisplay.getDisplayedMessages(tabId) : []; - if (!msgs.length) return; - const url = browser.runtime.getURL(`reasoning.html?mid=${msgs[0].id}`); - browser.tabs.create({ url }); - }); - toolbar.appendChild(button); - } - if (document.readyState === 'complete' || document.readyState === 'interactive') { - addButton(); - } else { - document.addEventListener('DOMContentLoaded', addButton, { once: true }); - } -})();