From db7ce49a5b47ea495681510396d7cbb26ed26343 Mon Sep 17 00:00:00 2001
From: Jordan Wages
Date: Sat, 19 Jul 2025 19:03:37 -0500
Subject: [PATCH] Add debug tab with payload viewer
---
README.md | 1 +
modules/AiClassifier.js | 5 +++++
options/options.html | 14 ++++++++++++++
options/options.js | 21 +++++++++++++++++++--
4 files changed, 39 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 3853127..43a515b 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,7 @@ message meets a specified criterion.
- **Advanced parameters** – tune generation settings like temperature, top‑p and more from the options page.
- **Markdown conversion** – optionally convert HTML bodies to Markdown before sending them to the AI service.
- **Debug logging** – optional colorized logs help troubleshoot interactions with the AI service.
+- **Debug tab** – view the last request payload sent to the AI service.
- **Light/Dark themes** – automatically match Thunderbird's appearance with optional manual override.
- **Automatic rules** – create rules that tag, move, copy, forward, reply, delete, archive, mark read/unread or flag/unflag messages based on AI classification. Rules can optionally apply only to unread messages and can ignore messages outside a chosen age range.
- **Rule ordering** – drag rules to prioritize them and optionally stop processing after a match.
diff --git a/modules/AiClassifier.js b/modules/AiClassifier.js
index b35cb2c..8313654 100644
--- a/modules/AiClassifier.js
+++ b/modules/AiClassifier.js
@@ -308,6 +308,11 @@ async function classifyText(text, criterion, cacheKey = null) {
}
const payload = buildPayload(text, criterion);
+ try {
+ await storage.local.set({ lastPayload: JSON.parse(payload) });
+ } catch (e) {
+ aiLog('failed to save last payload', { level: 'warn' }, e);
+ }
aiLog(`[AiClassifier] Sending classification request to ${gEndpoint}`, {debug: true});
aiLog(`[AiClassifier] Classification request payload:`, { debug: true }, payload);
diff --git a/options/options.html b/options/options.html
index 69158b1..d14ce99 100644
--- a/options/options.html
+++ b/options/options.html
@@ -47,6 +47,7 @@
Settings
Rules
Maintenance
+
Debug
@@ -215,6 +216,11 @@
+
+
+
@@ -273,6 +279,14 @@
+
+
+
+
+ Debug
+
+
+
diff --git a/options/options.js b/options/options.js
index ee2914a..ebd7497 100644
--- a/options/options.js
+++ b/options/options.js
@@ -19,7 +19,9 @@ document.addEventListener('DOMContentLoaded', async () => {
'tokenReduction',
'aiRules',
'aiCache',
- 'theme'
+ 'theme',
+ 'showDebugTab',
+ 'lastPayload'
]);
const tabButtons = document.querySelectorAll('#main-tabs li');
const tabs = document.querySelectorAll('.tab-content');
@@ -64,6 +66,10 @@ document.addEventListener('DOMContentLoaded', async () => {
}
await applyTheme(themeSelect.value);
+ const payloadDisplay = document.getElementById('payload-display');
+ if (defaults.lastPayload) {
+ payloadDisplay.textContent = JSON.stringify(defaults.lastPayload, null, 2);
+ }
themeSelect.addEventListener('change', async () => {
markDirty();
await applyTheme(themeSelect.value);
@@ -119,6 +125,16 @@ document.addEventListener('DOMContentLoaded', async () => {
const tokenReductionToggle = document.getElementById('token-reduction');
tokenReductionToggle.checked = defaults.tokenReduction === true;
+ const debugTabToggle = document.getElementById('show-debug-tab');
+ const debugTabBtn = document.getElementById('debug-tab-button');
+ function updateDebugTab() {
+ const visible = debugTabToggle.checked;
+ debugTabBtn.classList.toggle('is-hidden', !visible);
+ }
+ debugTabToggle.checked = defaults.showDebugTab === true;
+ debugTabToggle.addEventListener('change', () => { updateDebugTab(); markDirty(); });
+ updateDebugTab();
+
const aiParams = Object.assign({}, DEFAULT_AI_PARAMS, defaults.aiParams || {});
for (const [key, val] of Object.entries(aiParams)) {
@@ -797,8 +813,9 @@ document.addEventListener('DOMContentLoaded', async () => {
const altTextImages = altTextToggle.checked;
const collapseWhitespace = collapseWhitespaceToggle.checked;
const tokenReduction = tokenReductionToggle.checked;
+ const showDebugTab = debugTabToggle.checked;
const theme = themeSelect.value;
- await storage.local.set({ endpoint, templateName, customTemplate: customTemplateText, customSystemPrompt, aiParams: aiParamsSave, debugLogging, htmlToMarkdown, stripUrlParams, altTextImages, collapseWhitespace, tokenReduction, aiRules: rules, theme });
+ await storage.local.set({ endpoint, templateName, customTemplate: customTemplateText, customSystemPrompt, aiParams: aiParamsSave, debugLogging, htmlToMarkdown, stripUrlParams, altTextImages, collapseWhitespace, tokenReduction, aiRules: rules, theme, showDebugTab });
await applyTheme(theme);
try {
await AiClassifier.setConfig({ endpoint, templateName, customTemplate: customTemplateText, customSystemPrompt, aiParams: aiParamsSave, debugLogging });