diff --git a/README.md b/README.md index e6aecbe..3e60766 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ message meets a specified criterion. - **Context menu** – apply AI rules from the message list or the message display action button. - **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. +- **Maintenance tab** – view rule counts, cache entries and clear cached results from the options page. ### Cache Storage diff --git a/modules/AiClassifier.js b/modules/AiClassifier.js index a546fcd..de267b6 100644 --- a/modules/AiClassifier.js +++ b/modules/AiClassifier.js @@ -312,6 +312,17 @@ async function removeCacheEntries(keys = []) { } } +async function clearCache() { + if (!gCacheLoaded) { + await loadCache(); + } + if (gCache.size > 0) { + gCache.clear(); + await saveCache(); + aiLog(`[AiClassifier] Cleared cache`, {debug: true}); + } +} + function classifyTextSync(text, criterion, cacheKey = null) { if (!Services?.tm?.spinEventLoopUntil) { throw new Error("classifyTextSync requires Services"); @@ -396,4 +407,4 @@ async function init() { await loadCache(); } -export { classifyText, classifyTextSync, setConfig, removeCacheEntries, getReason, getCachedResult, buildCacheKey, buildCacheKeySync, init }; +export { classifyText, classifyTextSync, setConfig, removeCacheEntries, clearCache, getReason, getCachedResult, buildCacheKey, buildCacheKeySync, init }; diff --git a/options/options.html b/options/options.html index da9744d..f776fa2 100644 --- a/options/options.html +++ b/options/options.html @@ -46,6 +46,7 @@ @@ -171,6 +172,17 @@
+ + diff --git a/options/options.js b/options/options.js index 9bcf9dd..5536ad5 100644 --- a/options/options.js +++ b/options/options.js @@ -9,7 +9,8 @@ document.addEventListener('DOMContentLoaded', async () => { 'customSystemPrompt', 'aiParams', 'debugLogging', - 'aiRules' + 'aiRules', + 'aiCache' ]); const tabButtons = document.querySelectorAll('#main-tabs li'); const tabs = document.querySelectorAll('.tab-content'); @@ -300,6 +301,15 @@ document.addEventListener('DOMContentLoaded', async () => { if (r.stopProcessing) rule.stopProcessing = true; return rule; })); + + const ruleCountEl = document.getElementById('rule-count'); + const cacheCountEl = document.getElementById('cache-count'); + ruleCountEl.textContent = (defaults.aiRules || []).length; + cacheCountEl.textContent = defaults.aiCache ? Object.keys(defaults.aiCache).length : 0; + document.getElementById('clear-cache').addEventListener('click', async () => { + await AiClassifier.clearCache(); + cacheCountEl.textContent = '0'; + }); initialized = true; document.getElementById('save').addEventListener('click', async () => {