summarize_display_mode added. see #580
This commit is contained in:
parent
7bac86cc82
commit
58437ad168
8 changed files with 218 additions and 85 deletions
|
|
@ -1951,6 +1951,22 @@
|
|||
"message": "Choose whether to automatically generate summaries when viewing messages. Requires an API-based connection (not ChatGPT Web).",
|
||||
"description": ""
|
||||
},
|
||||
"prefs_OptionText_summarize_display_mode": {
|
||||
"message": "Display summary in",
|
||||
"description": ""
|
||||
},
|
||||
"prefs_OptionText_summarize_display_mode_inline": {
|
||||
"message": "Message pane (inline)",
|
||||
"description": ""
|
||||
},
|
||||
"prefs_OptionText_summarize_display_mode_webchat": {
|
||||
"message": "Chat window",
|
||||
"description": ""
|
||||
},
|
||||
"prefs_OptionText_summarize_display_mode_Info": {
|
||||
"message": "Choose where the summary result is displayed. Inline mode shows a summary banner directly in the message pane. Chat window mode opens the AI chat window.",
|
||||
"description": ""
|
||||
},
|
||||
"summarize_title": {
|
||||
"message": "Summary",
|
||||
"description": ""
|
||||
|
|
|
|||
|
|
@ -43,19 +43,32 @@ js/mzta-compose-script.js (inserts text into Thunderbird compose window)
|
|||
|
||||
### Data Flow: Inline Summary on Message Display
|
||||
|
||||
The `summarize_display_mode` preference (`'inline'` or `'webchat'`) controls where
|
||||
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 = 1` (manual button) respects `summarize_display_mode`:
|
||||
- `'inline'` → button click triggers inline generation
|
||||
- `'webchat'` → button click opens the AI chat window via `_openSummaryWebchat()`
|
||||
- Context menu summarize also respects `summarize_display_mode`:
|
||||
- `'inline'` with a single message → generates inline via `_generateSummaryForMessage()`
|
||||
- `'webchat'` or multiple messages → opens the AI chat window via `openChatGPT()`
|
||||
|
||||
```
|
||||
User opens/selects a message in Thunderbird
|
||||
↓
|
||||
mzta-compose-script.js (sends "initSummary" to background)
|
||||
↓
|
||||
mzta-background.js (checks summarize_auto pref)
|
||||
mzta-background.js (checks summarize_auto + summarize_display_mode prefs)
|
||||
↓
|
||||
┌────────────────────────────────────────────────┐
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ summarize_auto = 0 → do nothing │
|
||||
│ summarize_auto = 1 → show "click to generate" │
|
||||
│ summarize_auto = 2 → generate immediately │
|
||||
└────────────────────────────────────────────────┘
|
||||
↓ (if generating)
|
||||
│ summarize_auto = 1 → show "click to generate" button │
|
||||
│ display_mode = inline → click triggers inline gen │
|
||||
│ display_mode = webchat → click opens chat window │
|
||||
│ summarize_auto = 2 → generate immediately (always inline)│
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
↓ (if generating inline)
|
||||
taSummaryStore (check cache / set processing)
|
||||
↓ (cache miss)
|
||||
mzta-special-commands (via Web Worker, NOT chatgpt_web)
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ These are generated programmatically at the bottom of `mzta-options-default.js`
|
|||
| `spamfilter_show_msg_panel` | `true` | Show info panel on spam detection |
|
||||
| `summarize` | `false` | Enable email summarization |
|
||||
| `summarize_auto` | `0` | 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 Settings Page (`pages/summarize/`)
|
||||
|
||||
|
|
@ -106,7 +107,11 @@ The summarize settings page provides:
|
|||
- `0` (Disabled) — no inline summaries
|
||||
- `1` (Manual) — shows a "Click to generate summary" button in message display
|
||||
- `2` (Automatic) — generates summary immediately when message is opened
|
||||
3. **Three editable prompts** (used by context menu summarize, not inline):
|
||||
3. **Display mode dropdown** (`summarize_display_mode`) — controls where summaries are shown:
|
||||
- `'inline'` — summary banner in the message pane (default)
|
||||
- `'webchat'` — opens the AI chat window
|
||||
- Note: `summarize_auto = 2` always generates inline regardless of this setting. Context menu summarize with multiple messages always falls back to webchat.
|
||||
4. **Three editable prompts** (used by context menu summarize and webchat mode):
|
||||
- Summarize instruction prompt (`prompt_summarize`)
|
||||
- Email template prompt (`prompt_summarize_email_template`)
|
||||
- Email separator prompt (`prompt_summarize_email_separator`)
|
||||
|
|
|
|||
|
|
@ -851,7 +851,7 @@ switch (message.command) {
|
|||
triggerContainer.style.cursor = 'default';
|
||||
triggerText.textContent = browser.i18n.getMessage("summarize_generating");
|
||||
browser.runtime.sendMessage({
|
||||
command: "triggerSummaryGeneration",
|
||||
command: message.webchat ? "triggerSummaryWebchat" : "triggerSummaryGeneration",
|
||||
headerMessageId: message.headerMessageId
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -222,27 +222,41 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|||
async function _initSummary() {
|
||||
try {
|
||||
let tabId = sender.tab.id;
|
||||
let prefs = await browser.storage.sync.get({ summarize_auto: 0 });
|
||||
let prefs = await browser.storage.sync.get({ summarize_auto: 0, summarize_display_mode: prefs_default.summarize_display_mode });
|
||||
if (prefs.summarize_auto === 0) return;
|
||||
|
||||
let message = await browser.messageDisplay.getDisplayedMessage(tabId);
|
||||
if (!message) return;
|
||||
|
||||
// Auto mode (summarize_auto === 2) always generates inline
|
||||
if (prefs.summarize_auto === 2) {
|
||||
let cachedSummary = await summaryStore.loadSummary(message.headerMessageId);
|
||||
if (cachedSummary && !cachedSummary.error) {
|
||||
browser.tabs.sendMessage(tabId, { command: "showSummary", data: cachedSummary });
|
||||
return;
|
||||
}
|
||||
|
||||
if (await summaryStore.isProcessing(message.headerMessageId)) {
|
||||
browser.tabs.sendMessage(tabId, { command: "showSummaryGenerating" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (prefs.summarize_auto === 1) {
|
||||
browser.tabs.sendMessage(tabId, { command: "showSummaryButton", headerMessageId: message.headerMessageId });
|
||||
} else if (prefs.summarize_auto === 2) {
|
||||
_generateSummaryForMessage(message.headerMessageId, tabId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Manual button mode (summarize_auto === 1)
|
||||
if (prefs.summarize_display_mode === 'inline') {
|
||||
let cachedSummary = await summaryStore.loadSummary(message.headerMessageId);
|
||||
if (cachedSummary && !cachedSummary.error) {
|
||||
browser.tabs.sendMessage(tabId, { command: "showSummary", data: cachedSummary });
|
||||
return;
|
||||
}
|
||||
if (await summaryStore.isProcessing(message.headerMessageId)) {
|
||||
browser.tabs.sendMessage(tabId, { command: "showSummaryGenerating" });
|
||||
return;
|
||||
}
|
||||
browser.tabs.sendMessage(tabId, { command: "showSummaryButton", headerMessageId: message.headerMessageId });
|
||||
} else {
|
||||
browser.tabs.sendMessage(tabId, { command: "showSummaryButton", headerMessageId: message.headerMessageId, webchat: true });
|
||||
}
|
||||
} catch (e) {
|
||||
taLog.error("Error in initSummary: " + e);
|
||||
|
|
@ -257,6 +271,13 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|||
}
|
||||
_triggerSummaryGeneration(message);
|
||||
break;
|
||||
case 'triggerSummaryWebchat':
|
||||
async function _triggerSummaryWebchat(message) {
|
||||
let tabId = sender.tab.id;
|
||||
await _openSummaryWebchat(message.headerMessageId, tabId);
|
||||
}
|
||||
_triggerSummaryWebchat(message);
|
||||
break;
|
||||
case 'generate_summary':
|
||||
async function _generate_summary(message) {
|
||||
await _generateSummaryForMessage(message.headerMessageId, message.tabId);
|
||||
|
|
@ -514,6 +535,55 @@ async function _generateSummaryForMessage(headerMessageId, tabId) {
|
|||
}
|
||||
}
|
||||
|
||||
async function _openSummaryWebchat(headerMessageId, tabId) {
|
||||
try {
|
||||
const specialPrompts = await getSpecialPrompts();
|
||||
const prompt = specialPrompts.find(p => p.id === 'prompt_summarize');
|
||||
const prompt_email = specialPrompts.find(p => p.id === 'prompt_summarize_email_template');
|
||||
const prompt_email_separator = specialPrompts.find(p => p.id === 'prompt_summarize_email_separator');
|
||||
|
||||
const chatgpt_lang = await taPromptUtils.getDefaultLang(prompt);
|
||||
|
||||
const prompt_string = await taPromptUtils.preparePrompt({
|
||||
curr_prompt: prompt,
|
||||
chatgpt_lang: chatgpt_lang,
|
||||
});
|
||||
const prompt_email_separator_string = await taPromptUtils.preparePrompt({
|
||||
curr_prompt: prompt_email_separator,
|
||||
chatgpt_lang: chatgpt_lang,
|
||||
});
|
||||
|
||||
const messageResult = await browser.messages.query({ headerMessageId: headerMessageId });
|
||||
if (!messageResult || messageResult.messages.length === 0) {
|
||||
console.error("[ThunderAI] _openSummaryWebchat: Message not found for headerMessageId:", headerMessageId);
|
||||
return;
|
||||
}
|
||||
|
||||
const curr_message = messageResult.messages[0];
|
||||
const curr_message_full = await browser.messages.getFull(curr_message.id);
|
||||
const curr_body_full_html = getMailBody(curr_message_full);
|
||||
let curr_body_full_text = htmlBodyToPlainText(curr_body_full_html.html);
|
||||
if (curr_body_full_text.length === 0) {
|
||||
curr_body_full_text = curr_body_full_html.text;
|
||||
}
|
||||
|
||||
const email_text = await taPromptUtils.preparePrompt({
|
||||
curr_prompt: prompt_email,
|
||||
curr_message: curr_message,
|
||||
chatgpt_lang: chatgpt_lang,
|
||||
body_text: curr_body_full_text,
|
||||
subject_text: curr_message_full.headers.subject,
|
||||
msg_text: curr_body_full_html,
|
||||
});
|
||||
|
||||
const full_prompt = prompt_string + prompt_email_separator_string + email_text;
|
||||
|
||||
openChatGPT(full_prompt, prompt.action, tabId, prompt.name, prompt.need_custom_text, prompt);
|
||||
} catch (error) {
|
||||
console.error("[ThunderAI] Error opening summary webchat:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for messages from ThunderAI-Sparks
|
||||
browser.runtime.onMessageExternal.addListener((message, sender, sendResponse) => {
|
||||
switch (message.action) {
|
||||
|
|
@ -1384,6 +1454,21 @@ async function processEmails(args) {
|
|||
}
|
||||
|
||||
if (summarize) {
|
||||
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
const tabId = tabs[0].id;
|
||||
let summarize_prefs = await browser.storage.sync.get({ summarize_display_mode: prefs_default.summarize_display_mode });
|
||||
|
||||
// Collect messages into array to check count
|
||||
const messageArray = [];
|
||||
for await (let msg of messages) {
|
||||
messageArray.push(msg);
|
||||
}
|
||||
|
||||
// Inline mode for single message: generate inline summary in the message pane
|
||||
if (summarize_prefs.summarize_display_mode === 'inline' && messageArray.length === 1) {
|
||||
await _generateSummaryForMessage(messageArray[0].headerMessageId, tabId);
|
||||
} else {
|
||||
// Webchat mode, or inline with multiple messages (fallback to webchat)
|
||||
// we have three prompts, the actual assignment for the LLM, the email
|
||||
// template prompt, and the email separator prompt
|
||||
const specialPrompts = await getSpecialPrompts();
|
||||
|
|
@ -1391,7 +1476,6 @@ async function processEmails(args) {
|
|||
const prompt_email = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize_email_template');
|
||||
const prompt_email_separator = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize_email_separator');
|
||||
|
||||
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
const chatgpt_lang = await taPromptUtils.getDefaultLang(prompt);
|
||||
|
||||
// replace placeholders in the prompts the assignment prompt and email
|
||||
|
|
@ -1409,7 +1493,7 @@ async function processEmails(args) {
|
|||
|
||||
// assemble all email messages into one string and add the assignment prompt
|
||||
const messages_list = [];
|
||||
for await (let curr_message of messages) {
|
||||
for (let curr_message of messageArray) {
|
||||
|
||||
// extract body of current message as text
|
||||
const curr_message_full = await browser.messages.getFull(curr_message.id);
|
||||
|
|
@ -1433,18 +1517,17 @@ async function processEmails(args) {
|
|||
|
||||
const full_prompt = prompt_string + prompt_email_separator_string + messages_string;
|
||||
|
||||
// console.log(full_prompt);
|
||||
|
||||
// send the prompt to the chat interface
|
||||
openChatGPT(
|
||||
full_prompt,
|
||||
prompt.action,
|
||||
tabs[0].id,
|
||||
tabId,
|
||||
prompt.name,
|
||||
prompt.need_custom_text,
|
||||
prompt
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
taWorkingStatus.stopWorking();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ export const prefs_default = {
|
|||
spamfilter_threshold: 70,
|
||||
spamfilter_enabled_accounts: [],
|
||||
summarize_auto: 0, // 0: disabled, 1: manual button, 2: automatic
|
||||
summarize_display_mode: 'inline', // 'inline' or 'webchat'
|
||||
spamfilter_show_msg_panel: true,
|
||||
summarize: false,
|
||||
...generated_prefs
|
||||
|
|
|
|||
|
|
@ -39,6 +39,18 @@
|
|||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="summarize_tr">
|
||||
<td><span class="opt_title">__MSG_prefs_OptionText_summarize_display_mode__</span></td>
|
||||
<td>
|
||||
<label>
|
||||
<select id="summarize_display_mode" name="summarize_display_mode" class="option-input">
|
||||
<option value="inline">__MSG_prefs_OptionText_summarize_display_mode_inline__</option>
|
||||
<option value="webchat">__MSG_prefs_OptionText_summarize_display_mode_webchat__</option>
|
||||
</select>
|
||||
<br>__MSG_prefs_OptionText_summarize_display_mode_Info__
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- PROMPTS -->
|
||||
|
|
|
|||
|
|
@ -240,6 +240,9 @@ async function restoreOptions() {
|
|||
if (element.id === 'summarize_auto') {
|
||||
default_select_value = prefs_default.summarize_auto;
|
||||
}
|
||||
if (element.id === 'summarize_display_mode') {
|
||||
default_select_value = prefs_default.summarize_display_mode;
|
||||
}
|
||||
const restoreValue = result[element.id] ?? default_select_value;
|
||||
// Check if option exists
|
||||
let optionExists = Array.from(element.options).some(opt => opt.value === String(restoreValue));
|
||||
|
|
|
|||
Loading…
Reference in a new issue