Update icons and options UI

This commit is contained in:
Jordan Wages 2025-07-08 00:40:40 -05:00
commit 9a4b1ce239
5 changed files with 103 additions and 19 deletions

View file

@ -66,3 +66,10 @@ text extracted from all text parts.
properties. Any legacy `aiReasonCache` data is merged into `aiCache` the first
time the add-on loads after an update.
### Icon Set Usage
Toolbar and menu icons reside under `resources/img` and are provided in 16, 32
and 64 pixel variants. When changing these icons, pass a dictionary mapping the
sizes to the paths in `browserAction.setIcon` or `messageDisplayAction.setIcon`.
Use `resources/svg/svg2img.ps1` to regenerate PNGs from the SVG sources.

View file

@ -141,3 +141,5 @@ Sortana builds upon knowledge gained from open-source projects. In particular,
how Thunderbird's WebExtension and experiment APIs can be extended. Their code
provided invaluable guidance during development.
- Icons from [cc0-icons.jonh.eu](https://cc0-icons.jonh.eu/) are used under the CC0 license.

View file

@ -28,6 +28,25 @@ let altTextImages = false;
let collapseWhitespace = false;
let TurndownService = null;
const ICONS = {
logo: "resources/img/logo.png",
circledots: {
16: "resources/img/circledots-16.png",
32: "resources/img/circledots-32.png",
64: "resources/img/circledots-64.png"
},
circle: {
16: "resources/img/circle-16.png",
32: "resources/img/circle-32.png",
64: "resources/img/circle-64.png"
},
average: {
16: "resources/img/average-16.png",
32: "resources/img/average-32.png",
64: "resources/img/average-64.png"
}
};
function setIcon(path) {
if (browser.browserAction) {
browser.browserAction.setIcon({ path });
@ -38,9 +57,9 @@ function setIcon(path) {
}
function updateActionIcon() {
let path = "resources/img/brain.png";
let path = ICONS.logo;
if (processing || queuedCount > 0) {
path = "resources/img/busy.png";
path = ICONS.circledots;
}
setIcon(path);
}
@ -201,7 +220,7 @@ async function applyAiRules(idsInput) {
t.mean += delta / t.count;
t.m2 += delta * (elapsed - t.mean);
await storage.local.set({ classifyStats: t });
showTransientIcon("resources/img/done.png");
showTransientIcon(ICONS.circle);
} catch (e) {
processing = false;
const elapsed = Date.now() - currentStart;
@ -215,7 +234,7 @@ async function applyAiRules(idsInput) {
t.m2 += delta * (elapsed - t.mean);
await storage.local.set({ classifyStats: t });
logger.aiLog("failed to apply AI rules", { level: 'error' }, e);
showTransientIcon("resources/img/error.png");
showTransientIcon(ICONS.average);
}
});
}
@ -250,7 +269,7 @@ async function clearCacheForMessages(idsInput) {
}
if (keys.length) {
await AiClassifier.removeCacheEntries(keys);
showTransientIcon("resources/img/done.png");
showTransientIcon(ICONS.circle);
}
}
@ -340,33 +359,61 @@ async function clearCacheForMessages(idsInput) {
id: "apply-ai-rules-list",
title: "Apply AI Rules",
contexts: ["message_list"],
icons: {
16: "resources/img/eye-16.png",
32: "resources/img/eye-32.png",
64: "resources/img/eye-64.png"
}
});
browser.menus.create({
id: "apply-ai-rules-display",
title: "Apply AI Rules",
contexts: ["message_display_action"],
icons: {
16: "resources/img/eye-16.png",
32: "resources/img/eye-32.png",
64: "resources/img/eye-64.png"
}
});
browser.menus.create({
id: "clear-ai-cache-list",
title: "Clear AI Cache",
contexts: ["message_list"],
icons: {
16: "resources/img/trash-16.png",
32: "resources/img/trash-32.png",
64: "resources/img/trash-64.png"
}
});
browser.menus.create({
id: "clear-ai-cache-display",
title: "Clear AI Cache",
contexts: ["message_display_action"],
icons: {
16: "resources/img/trash-16.png",
32: "resources/img/trash-32.png",
64: "resources/img/trash-64.png"
}
});
browser.menus.create({
id: "view-ai-reason-list",
title: "View Reasoning",
contexts: ["message_list"],
icons: { "16": "resources/img/brain.png" }
icons: {
16: "resources/img/clipboarddata-16.png",
32: "resources/img/clipboarddata-32.png",
64: "resources/img/clipboarddata-64.png"
}
});
browser.menus.create({
id: "view-ai-reason-display",
title: "View Reasoning",
contexts: ["message_display_action"],
icons: { "16": "resources/img/brain.png" }
icons: {
16: "resources/img/clipboarddata-16.png",
32: "resources/img/clipboarddata-32.png",
64: "resources/img/clipboarddata-64.png"
}
});
browser.menus.onClicked.addListener(async (info, tab) => {

View file

@ -22,7 +22,7 @@
"default_icon": "resources/img/logo32.png"
},
"message_display_action": {
"default_icon": "resources/img/brain.png",
"default_icon": "resources/img/logo.png",
"default_title": "Details",
"default_label": "Details",
"default_popup": "details.html"

View file

@ -44,18 +44,25 @@
<div class="level-left">
<div class="tabs" id="main-tabs">
<ul>
<li class="is-active" data-tab="settings"><a>Settings</a></li>
<li data-tab="rules"><a>Rules</a></li>
<li data-tab="maintenance"><a>Maintenance</a></li>
<li class="is-active" data-tab="settings"><a><span class="icon is-small"><img src="../resources/svg/settings.svg" alt=""></span><span>Settings</span></a></li>
<li data-tab="rules"><a><span class="icon is-small"><img src="../resources/svg/clipboarddata.svg" alt=""></span><span>Rules</span></a></li>
<li data-tab="maintenance"><a><span class="icon is-small"><img src="../resources/svg/gear.svg" alt=""></span><span>Maintenance</span></a></li>
</ul>
</div>
</div>
<div class="level-right">
<button class="button is-primary" id="save" disabled>Save</button>
<button class="button is-primary" id="save" disabled>
<span class="icon is-small"><img src="../resources/svg/flag.svg" alt=""></span>
<span>Save</span>
</button>
</div>
</div>
<div id="settings-tab" class="tab-content">
<h2 class="title is-4">
<span class="icon is-small"><img src="../resources/svg/settings.svg" alt=""></span>
<span>Settings</span>
</h2>
<div class="field">
<label class="label" for="endpoint">Endpoint</label>
<div class="control">
@ -88,8 +95,14 @@
</div>
<div class="buttons">
<button class="button is-danger" id="reset-system">Reset to default</button>
<button class="button" id="toggle-advanced" type="button">Advanced</button>
<button class="button is-danger" id="reset-system">
<span class="icon is-small"><img src="../resources/svg/reply.svg" alt=""></span>
<span>Reset to default</span>
</button>
<button class="button" id="toggle-advanced" type="button">
<span class="icon is-small"><img src="../resources/svg/gear.svg" alt=""></span>
<span>Advanced</span>
</button>
</div>
<div id="advanced-options" class="mt-4 is-hidden">
@ -188,13 +201,19 @@
</div>
<div id="rules-tab" class="tab-content is-hidden">
<h2 class="title is-4">Classification Rules</h2>
<h2 class="title is-4">
<span class="icon is-small"><img src="../resources/svg/clipboarddata.svg" alt=""></span>
<span>Classification Rules</span>
</h2>
<div id="rules-container"></div>
<button class="button is-link" id="add-rule" type="button">Add Rule</button>
</div>
<div id="maintenance-tab" class="tab-content is-hidden">
<h2 class="title is-4">Maintenance</h2>
<h2 class="title is-4">
<span class="icon is-small"><img src="../resources/svg/gear.svg" alt=""></span>
<span>Maintenance</span>
</h2>
<table class="table is-fullwidth">
<tbody>
<tr><th>Rule count</th><td id="rule-count"></td></tr>
@ -206,7 +225,10 @@
<tr><th>Total run time</th><td id="total-time">--:--:--</td></tr>
</tbody>
</table>
<button class="button is-danger" id="clear-cache" type="button">Clear Cache</button>
<button class="button is-danger" id="clear-cache" type="button">
<span class="icon is-small"><img src="../resources/svg/trash.svg" alt=""></span>
<span>Clear Cache</span>
</button>
<div class="field mt-4">
<label class="label">Data categories</label>
<div class="control">
@ -217,10 +239,16 @@
</div>
<div class="field is-grouped mt-4">
<p class="control">
<button class="button" id="export-data" type="button">Export Data</button>
<button class="button" id="export-data" type="button">
<span class="icon is-small"><img src="../resources/svg/download.svg" alt=""></span>
<span>Export Data</span>
</button>
</p>
<p class="control">
<button class="button" id="import-data" type="button">Import Data</button>
<button class="button" id="import-data" type="button">
<span class="icon is-small"><img src="../resources/svg/upload.svg" alt=""></span>
<span>Import Data</span>
</button>
<input class="is-hidden" type="file" id="import-file" accept="application/json">
</p>
</div>