parent
61916c1a85
commit
a8c69d4426
6 changed files with 86 additions and 27 deletions
|
|
@ -1966,7 +1966,7 @@
|
|||
"description": ""
|
||||
},
|
||||
"prefs_OptionText_summarize_auto_automatic": {
|
||||
"message": "Generate automatically",
|
||||
"message": "When the email is opened",
|
||||
"description": ""
|
||||
},
|
||||
"prefs_OptionText_summarize_auto_Info": {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ The `summarize_display_mode` preference (`'inline'` or `'webchat'`) controls whe
|
|||
the summary is displayed. The `summarize_auto` preference controls when it is triggered.
|
||||
|
||||
- `summarize_auto = 2` (automatic) always generates inline, regardless of `summarize_display_mode`.
|
||||
- `summarize_auto = 3` (on receive) pre-caches the summary silently when the email arrives via `onNewMailReceived`. When the user later opens the message, the cache hit triggers an instant display.
|
||||
- `summarize_auto = 1` (manual button) respects `summarize_display_mode`:
|
||||
- `'inline'` → button click triggers inline generation
|
||||
- `'webchat'` → button click opens the AI chat window via `_openSummaryWebchat()`
|
||||
|
|
@ -67,6 +68,7 @@ mzta-background.js (checks summarize_auto + summarize_display_mode prefs)
|
|||
│ display_mode = inline → click triggers inline gen │
|
||||
│ display_mode = webchat → click opens chat window │
|
||||
│ summarize_auto = 2 → generate immediately (always inline)│
|
||||
│ summarize_auto = 3 → cache hit (pre-cached on receive) │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
↓ (if generating inline)
|
||||
taSummaryStore (check cache / set processing)
|
||||
|
|
@ -78,6 +80,27 @@ mzta-background.js (checks summarize_auto + summarize_display_mode prefs)
|
|||
mzta-compose-script.js (render summary banner in message body)
|
||||
```
|
||||
|
||||
### Data Flow: Background Summary on Email Receive (summarize_auto = 3)
|
||||
|
||||
When `summarize_auto = 3`, a summary is generated silently when a new email arrives. The flow mirrors `add_tags_auto`:
|
||||
|
||||
```
|
||||
New email arrives
|
||||
↓
|
||||
browser.messages.onNewMailReceived
|
||||
↓
|
||||
newEmailListener (checks _process_incoming, which includes summarize_auto === 3)
|
||||
↓
|
||||
processEmails({ summarizeOnReceive: true })
|
||||
↓ (single loop — shared with addTagsAuto / spamFilter)
|
||||
_generateSummaryForMessage(headerMessageId, null, { messageData })
|
||||
← tabId is null → no UI messages sent, silent pre-cache
|
||||
↓
|
||||
taSummaryStore.saveSummary()
|
||||
↓
|
||||
[later] user opens the message → initSummary → cache hit → showSummary instantly
|
||||
```
|
||||
|
||||
## Key Modules
|
||||
|
||||
| File | Role |
|
||||
|
|
|
|||
|
|
@ -95,8 +95,8 @@ These are generated programmatically at the bottom of `mzta-options-default.js`
|
|||
| `spamfilter_enabled_accounts` | `[]` | Accounts where spam filter is active |
|
||||
| `spamfilter_show_msg_panel` | `true` | Show info panel on spam detection |
|
||||
| `summarize` | `false` | Enable email summarization |
|
||||
| `summarize_auto` | `1` | Auto-summarize mode: `0` = disabled, `1` = manual (show "click to generate" button), `2` = automatic (generate on message open) |
|
||||
| `summarize_display_mode` | `'inline'` | Where to display summaries: `'inline'` = message pane banner, `'webchat'` = AI chat window. Note: `summarize_auto = 2` always uses inline regardless of this setting. |
|
||||
| `summarize_auto` | `1` | Auto-summarize mode: `0` = disabled, `1` = manual (show "click to generate" button), `2` = automatic (generate on message open), `3` = generate on email receive (background pre-cache via `onNewMailReceived`, no UI during generation) |
|
||||
| `summarize_display_mode` | `'inline'` | Where to display summaries: `'inline'` = message pane banner, `'webchat'` = AI chat window. Note: `summarize_auto = 2` and `summarize_auto = 3` always use inline regardless of this setting. |
|
||||
| `summarize_max_display_length` | `0` | Maximum characters shown in inline summary before truncation. `0` = no limit (show full text). When set, text is truncated at a word boundary and a "See more"/"See less" toggle link is shown. |
|
||||
|
||||
### Summarize Settings Page (`pages/summarize/`)
|
||||
|
|
|
|||
|
|
@ -505,7 +505,9 @@ function cleanSummaryText(text) {
|
|||
return cleaned;
|
||||
}
|
||||
|
||||
async function _generateSummaryForMessage(headerMessageId, tabId) {
|
||||
// tabId is optional — if null, runs silently (background pre-cache, no UI update)
|
||||
// options.messageData: { message, fullMessage } — pass pre-fetched data to avoid re-querying
|
||||
async function _generateSummaryForMessage(headerMessageId, tabId = null, options = {}) {
|
||||
try {
|
||||
let prefs = await browser.storage.sync.get({
|
||||
connection_type: prefs_default.connection_type,
|
||||
|
|
@ -517,40 +519,46 @@ async function _generateSummaryForMessage(headerMessageId, tabId) {
|
|||
|
||||
let cachedSummary = await summaryStore.loadSummary(headerMessageId);
|
||||
if (cachedSummary && !cachedSummary.error) {
|
||||
browser.tabs.sendMessage(tabId, { command: "showSummary", data: { ...cachedSummary, maxDisplayLength: prefs.summarize_max_display_length } });
|
||||
if (tabId) browser.tabs.sendMessage(tabId, { command: "showSummary", data: { ...cachedSummary, maxDisplayLength: prefs.summarize_max_display_length } });
|
||||
return;
|
||||
}
|
||||
|
||||
if (await summaryStore.isProcessing(headerMessageId)) {
|
||||
browser.tabs.sendMessage(tabId, { command: "showSummaryGenerating" });
|
||||
if (tabId) browser.tabs.sendMessage(tabId, { command: "showSummaryGenerating" });
|
||||
return;
|
||||
}
|
||||
|
||||
await summaryStore.setProcessing(headerMessageId);
|
||||
taWorkingStatus.startWorking();
|
||||
browser.tabs.sendMessage(tabId, { command: "showSummaryGenerating" });
|
||||
if (tabId) browser.tabs.sendMessage(tabId, { command: "showSummaryGenerating" });
|
||||
|
||||
let message, fullMessage;
|
||||
if (options.messageData) {
|
||||
message = options.messageData.message;
|
||||
fullMessage = options.messageData.fullMessage;
|
||||
} else {
|
||||
const messageResult = await browser.messages.query({ headerMessageId: headerMessageId });
|
||||
if (!messageResult || messageResult.messages.length === 0) {
|
||||
await summaryStore.saveError(headerMessageId, "Message not found");
|
||||
browser.tabs.sendMessage(tabId, { command: "showSummary", data: { error: true, message: "Message not found" } });
|
||||
if (tabId) browser.tabs.sendMessage(tabId, { command: "showSummary", data: { error: true, message: "Message not found" } });
|
||||
taWorkingStatus.stopWorking();
|
||||
return;
|
||||
}
|
||||
|
||||
const fullMessage = await browser.messages.getFull(messageResult.messages[0].id);
|
||||
message = messageResult.messages[0];
|
||||
fullMessage = await browser.messages.getFull(message.id);
|
||||
}
|
||||
|
||||
const connectionType = getConnectionType(prefs, {}, 'summarize');
|
||||
|
||||
if (connectionType === 'chatgpt_web') {
|
||||
const errorMsg = browser.i18n.getMessage('summarize_chatgpt_web_not_supported');
|
||||
await summaryStore.saveError(headerMessageId, errorMsg);
|
||||
browser.tabs.sendMessage(tabId, { command: "showSummary", data: { error: true, message: errorMsg } });
|
||||
if (tabId) browser.tabs.sendMessage(tabId, { command: "showSummary", data: { error: true, message: errorMsg } });
|
||||
taWorkingStatus.stopWorking();
|
||||
return;
|
||||
}
|
||||
|
||||
const { promptText } = await taPromptUtils.buildSummaryPrompt([{ message: messageResult.messages[0], fullMessage }]);
|
||||
const { promptText } = await taPromptUtils.buildSummaryPrompt([{ message, fullMessage }]);
|
||||
|
||||
const cmd = new mzta_specialCommand({
|
||||
prompt: promptText,
|
||||
|
|
@ -572,13 +580,13 @@ async function _generateSummaryForMessage(headerMessageId, tabId) {
|
|||
headerMessageId: headerMessageId
|
||||
};
|
||||
await summaryStore.saveSummary(summaryData, headerMessageId);
|
||||
browser.tabs.sendMessage(tabId, { command: "showSummary", data: { ...summaryData, maxDisplayLength: prefs.summarize_max_display_length } });
|
||||
if (tabId) browser.tabs.sendMessage(tabId, { command: "showSummary", data: { ...summaryData, maxDisplayLength: prefs.summarize_max_display_length } });
|
||||
taWorkingStatus.stopWorking();
|
||||
|
||||
} catch (error) {
|
||||
console.error("[ThunderAI] Error generating summary:", error);
|
||||
await summaryStore.saveError(headerMessageId, error.message || String(error));
|
||||
browser.tabs.sendMessage(tabId, { command: "showSummary", data: { error: true, message: error.message || "Failed to generate summary" } });
|
||||
if (tabId) browser.tabs.sendMessage(tabId, { command: "showSummary", data: { error: true, message: error.message || "Failed to generate summary" } });
|
||||
taWorkingStatus.stopWorking();
|
||||
}
|
||||
}
|
||||
|
|
@ -1162,13 +1170,14 @@ async function reload_pref_init(){
|
|||
add_tags_auto_only_inbox: prefs_default.add_tags_auto_only_inbox,
|
||||
spamfilter: prefs_default.spamfilter,
|
||||
summarize: prefs_default.summarize,
|
||||
summarize_auto: prefs_default.summarize_auto,
|
||||
spamfilter_threshold: prefs_default.spamfilter_threshold,
|
||||
spamfilter_show_msg_panel: prefs_default.spamfilter_show_msg_panel,
|
||||
dynamic_menu_force_enter: prefs_default.dynamic_menu_force_enter,
|
||||
chatgpt_win_save_position: prefs_default.chatgpt_win_save_position,
|
||||
...getDynamicSettingsDefaults(['use_specific_integration', 'connection_type'])
|
||||
});
|
||||
_process_incoming = prefs_init.add_tags_auto || prefs_init.spamfilter;
|
||||
_process_incoming = prefs_init.add_tags_auto || prefs_init.spamfilter || (prefs_init.summarize && prefs_init.summarize_auto === 3);
|
||||
_sparks_presence = await checkSparksPresence();
|
||||
}
|
||||
|
||||
|
|
@ -1395,7 +1404,8 @@ const newEmailListener = (folder, messagesList) => {
|
|||
await processEmails({
|
||||
messages: messages,
|
||||
addTagsAuto: add_tags_auto_enabled,
|
||||
spamFilter: prefs_init.spamfilter
|
||||
spamFilter: prefs_init.spamfilter,
|
||||
summarizeOnReceive: prefs_init.summarize && prefs_init.summarize_auto === 3
|
||||
});
|
||||
|
||||
if(prefs_init.spamfilter){
|
||||
|
|
@ -1428,15 +1438,16 @@ async function processEmails(args) {
|
|||
messages,
|
||||
addTagsAuto = false,
|
||||
spamFilter = false,
|
||||
summarize = false
|
||||
summarize = false,
|
||||
summarizeOnReceive = false
|
||||
} = args;
|
||||
|
||||
taWorkingStatus.startWorking();
|
||||
|
||||
// We keep two different loops, one for addTagsAuto and spamFilter and one for summarize
|
||||
// because summarize is never called when an email is received, but only when using the context menu item
|
||||
// One loop handles addTagsAuto, spamFilter, and summarizeOnReceive (on email receive).
|
||||
// The separate summarize block below handles the context menu flow.
|
||||
|
||||
if (addTagsAuto || spamFilter) {
|
||||
if (addTagsAuto || spamFilter || summarizeOnReceive) {
|
||||
let prefs_aats = await browser.storage.sync.get({
|
||||
add_tags_maxnum: prefs_default.add_tags_maxnum,
|
||||
connection_type: prefs_default.connection_type,
|
||||
|
|
@ -1531,6 +1542,16 @@ async function processEmails(args) {
|
|||
});
|
||||
if (!result.success) continue;
|
||||
}
|
||||
|
||||
if (summarizeOnReceive) {
|
||||
if (!curr_fullMessage) {
|
||||
curr_fullMessage = await browser.messages.getFull(message.id);
|
||||
}
|
||||
taLog.log("[ThunderAI] Pre-caching summary on receive for: " + message.headerMessageId);
|
||||
await _generateSummaryForMessage(message.headerMessageId, null, {
|
||||
messageData: { message, fullMessage: curr_fullMessage }
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ export const prefs_default = {
|
|||
spamfilter_threshold: 70,
|
||||
spamfilter_enabled_accounts: [],
|
||||
summarize: false,
|
||||
summarize_auto: 1, // 0: disabled, 1: manual button, 2: automatic, 3: batch processing
|
||||
summarize_auto: 1, // 0: disabled, 1: manual button, 2: automatic on message open, 3: generate on email receive
|
||||
summarize_display_mode: 'inline', // 'inline' or 'webchat'
|
||||
summarize_max_display_length: 0, // 0 = no limit, otherwise max chars shown inline
|
||||
spamfilter_show_msg_panel: true,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||
document.querySelectorAll(".option-input").forEach(element => {
|
||||
element.addEventListener("change", saveOptions);
|
||||
});
|
||||
document.getElementById('summarize_auto').addEventListener('change', updateDisplayModeConstraint);
|
||||
let prefs_summarize = await browser.storage.sync.get({ summarize_enabled_accounts: [], connection_type: 'chatgpt_web' });
|
||||
|
||||
let summarize_textarea = document.getElementById("summarize_prompt_text");
|
||||
|
|
@ -180,6 +181,19 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||
|
||||
// Methods to manage options, derived from: /options/mzta-options.js
|
||||
|
||||
function updateDisplayModeConstraint() {
|
||||
const summarize_auto_el = document.getElementById('summarize_auto');
|
||||
const display_mode_el = document.getElementById('summarize_display_mode');
|
||||
const autoVal = String(summarize_auto_el.value);
|
||||
if (autoVal === '2' || autoVal === '3') {
|
||||
display_mode_el.value = 'inline';
|
||||
display_mode_el.disabled = true;
|
||||
browser.storage.sync.set({ summarize_display_mode: 'inline' });
|
||||
} else {
|
||||
display_mode_el.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function saveOptions(e) {
|
||||
e.preventDefault();
|
||||
let options = {};
|
||||
|
|
@ -294,4 +308,5 @@ async function restoreOptions() {
|
|||
}
|
||||
|
||||
setCurrentChoice(getting);
|
||||
updateDisplayModeConstraint();
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue