From 84a65acd8fab550f1ae9197abe10356689fde724 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Sat, 3 Jan 2026 11:49:37 +0100 Subject: [PATCH 01/26] resolve rebase conflicts --- _locales/en/messages.json | 16 ++++++++++++++++ js/mzta-utils.js | 2 ++ mzta-background.js | 18 ++++++++++++++++++ options/mzta-options-default.js | 2 ++ options/mzta-options.html | 13 ++++++++++++- 5 files changed, 50 insertions(+), 1 deletion(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 1e8b9cac..432b6f34 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1195,6 +1195,18 @@ "message": "Manage spam filter settings", "description": "" }, + "prefs_OptionText_summarize": { + "message": "Summarize mail", + "description": "" + }, + "prefs_OptionText_summarize_Info": { + "message": "If checked, adds an option to context menu to summarize mail.", + "description": "" + }, + "prefs_OptionText_btnManageSummarizeInfo": { + "message": "Manage summarize settings", + "description": "" + }, "SpamFilter_PageTitle": { "message": "Manage Spam Filter Settings", "description": "" @@ -1295,6 +1307,10 @@ "message": "Analyze for spam", "description": "" }, + "context_menu_mzta-summary": { + "message": "Summarize", + "description": "" + }, "prefs_OptionText_add_tags_context_menu": { "message": "Show the \"Add tags\" context menu item", "description": "" diff --git a/js/mzta-utils.js b/js/mzta-utils.js index 67ce0823..79216f9d 100644 --- a/js/mzta-utils.js +++ b/js/mzta-utils.js @@ -25,9 +25,11 @@ export const getMenuContextDisplay = () => 'message_display_action_menu'; export const contextMenuID_AddTags = 'mzta-add-tags'; export const contextMenuID_Spamfilter = 'mzta-spamfilter'; +export const contextMenuID_Summary = 'mzta-summary'; export const contextMenuIconsPath = { [contextMenuID_AddTags]: 'moz-extension:images/autotags.png', [contextMenuID_Spamfilter]: 'moz-extension:images/spamfilter.png', + // [contextMenuID_Summary]: 'moz-extension:images/summary.png', }; export function getLanguageDisplayName(languageCode) { diff --git a/mzta-background.js b/mzta-background.js index e2161b66..ce3d1d7b 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -45,6 +45,7 @@ import { extractJsonObject, contextMenuID_AddTags, contextMenuID_Spamfilter, + contextMenuID_Summary, contextMenuIconsPath, sanitizeChatGPTModelData, sanitizeChatGPTWebCustomData, @@ -802,10 +803,12 @@ async function reload_pref_init(){ add_tags_auto_force_existing: prefs_default.add_tags_auto_force_existing, add_tags_auto_only_inbox: prefs_default.add_tags_auto_only_inbox, spamfilter: prefs_default.spamfilter, + summarize: prefs_default.summarize, spamfilter_threshold: prefs_default.spamfilter_threshold, dynamic_menu_force_enter: prefs_default.dynamic_menu_force_enter, add_tags_context_menu: prefs_default.add_tags_context_menu, spamfilter_context_menu: prefs_default.spamfilter_context_menu, + summarize_context_menu: prefs_default.summarize_context_menu, ...getDynamicSettingsDefaults(['use_specific_integration', 'connection_type']) }); _process_incoming = prefs_init.add_tags_auto || prefs_init.spamfilter; @@ -917,6 +920,15 @@ function setupStorageChangeListener() { removeContextMenu(contextMenuID_Spamfilter); } } + if (changes.summarize) { + if (changes.summarize.newValue){ + if (prefs_init.summarize_context_menu){ + addContextMenu(contextMenuID_Summary); + } + } else { + removeContextMenu(contextMenuID_Summary); + } + } reload_pref_init(); } }); @@ -977,6 +989,12 @@ function addContextMenuItems() { if(prefs_init.spamfilter && prefs_init.spamfilter_context_menu && checkAPIIntegration(prefs_init.connection_type, prefs_init.spamfilter_use_specific_integration,prefs_init.spamfilter_connection_type)){ addContextMenu(contextMenuID_Spamfilter); } + + // Add Context menu: Summary + if(prefs_init.summarize && prefs_init.summarize_context_menu && checkAPIIntegration(prefs_init.connection_type && prefs_init.summarize_use_specific_integration, prefs_init.summarize_connection_type)) { + console.log("adding summary to context menu") + addContextMenu(contextMenuID_Summary); + } } addContextMenuItems(); diff --git a/options/mzta-options-default.js b/options/mzta-options-default.js index 66b24890..e14b2808 100644 --- a/options/mzta-options-default.js +++ b/options/mzta-options-default.js @@ -133,5 +133,7 @@ export const prefs_default = { spamfilter_threshold: 70, spamfilter_context_menu: true, spamfilter_enabled_accounts: [], + summarize: false, + summarize_context_menu: true, ...generated_prefs } diff --git a/options/mzta-options.html b/options/mzta-options.html index 0e24f172..99078369 100644 --- a/options/mzta-options.html +++ b/options/mzta-options.html @@ -147,6 +147,17 @@ + + __MSG_prefs_OptionText_summarize__ +
+ + + + " __MSG_prefs_OptionText_get_calendar_event__
@@ -164,7 +175,7 @@ + From 050a3fd1f75e794cc885a0398b0c6d77f2de7b15 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Fri, 19 Dec 2025 10:00:10 +0100 Subject: [PATCH 02/26] Add MZTA summary page --- pages/summary/mzta-summary.css | 0 pages/summary/mzta-summary.html | 0 pages/summary/mzta-summary.js | 0 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 pages/summary/mzta-summary.css create mode 100644 pages/summary/mzta-summary.html create mode 100644 pages/summary/mzta-summary.js diff --git a/pages/summary/mzta-summary.css b/pages/summary/mzta-summary.css new file mode 100644 index 00000000..e69de29b diff --git a/pages/summary/mzta-summary.html b/pages/summary/mzta-summary.html new file mode 100644 index 00000000..e69de29b diff --git a/pages/summary/mzta-summary.js b/pages/summary/mzta-summary.js new file mode 100644 index 00000000..e69de29b From 4ab6da2faf08ad0e1e4e357ccc306e7724bff2c6 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Fri, 19 Dec 2025 13:09:25 +0100 Subject: [PATCH 03/26] change all "summary" -> "summarize" --- _locales/en/messages.json | 22 ++++- js/mzta-utils.js | 4 +- mzta-background.js | 8 +- options/mzta-options.js | 4 + pages/summarize/mzta-summarize.css | 123 ++++++++++++++++++++++++++++ pages/summarize/mzta-summarize.html | 30 +++++++ pages/summarize/mzta-summarize.js | 77 +++++++++++++++++ pages/summary/mzta-summary.css | 0 pages/summary/mzta-summary.html | 0 pages/summary/mzta-summary.js | 0 10 files changed, 261 insertions(+), 7 deletions(-) create mode 100644 pages/summarize/mzta-summarize.css create mode 100644 pages/summarize/mzta-summarize.html create mode 100644 pages/summarize/mzta-summarize.js delete mode 100644 pages/summary/mzta-summary.css delete mode 100644 pages/summary/mzta-summary.html delete mode 100644 pages/summary/mzta-summary.js diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 432b6f34..c98079bd 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1003,6 +1003,14 @@ "message": "Extract all relevant details required to generate a calendar event from the following text. The extracted information should include:\n- Event Title\n- Start Date and Time (including timezone, if specified)\n- End Date and Time (including timezone, if specified)\n- Full day (if mentioned)\n- Attendees\nEnsure the data is formatted clearly and consistently so that it can be directly used for creating a calendar event.\nIf there are relative time references, consider that the date and time of the email are \"{%mail_datetime%}\". Calculate the start date and time based on this reference. If the calculated start date and time are earlier than \"{%current_datetime%}\", recalculate the start date and time using \"{%current_datetime%}\" as the base.\nIf the duration is not specified, set it to one hour.\nThese are the attendees: {%author%}, {%recipients%}, {%cc_list%}. If present, exclude my address: {%account_email_address%}.\nIf you're not able to get one or more of the required information, please respond with an empty string.\nGenerate a response in JSON format only. Do not include any additional text or explanations; provide only the JSON. Here is the format to be used:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"Calendar event summary here\",\n\"forceAllDay\": false,\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nHere's the text:\"{%selected_text%}\"", "description": "" }, + "prompt_summarize": { + "message": "Summarize this email or these emails", + "description": "" + }, + "prompt_summarize_full_text": { + "message": "Summarize the following email or emails into a bullet point list. If there are multiple emails, consider the entire thread for the summary.", + "description": "" + }, "prompt_get_task": { "message": "Add a new task", "description": "" @@ -1231,6 +1239,18 @@ "message": "Spam Filter Options", "description": "" }, + "Summarize_PageTitle": { + "message": "Manage Summarize Settings", + "description": "" + }, + "Summarize_info_default": { + "message": "In this page you can modify the default prompt used to summarize emails.", + "description": "" + }, + "Summarize_prompt_text_title": { + "message": "Current prompt text", + "description": "" + }, "prefs_OptionText_use_specific_integration": { "message": "Use specific Model and API", "description": "" @@ -1307,7 +1327,7 @@ "message": "Analyze for spam", "description": "" }, - "context_menu_mzta-summary": { + "context_menu_mzta-summarize": { "message": "Summarize", "description": "" }, diff --git a/js/mzta-utils.js b/js/mzta-utils.js index 79216f9d..eb4f8d49 100644 --- a/js/mzta-utils.js +++ b/js/mzta-utils.js @@ -25,11 +25,11 @@ export const getMenuContextDisplay = () => 'message_display_action_menu'; export const contextMenuID_AddTags = 'mzta-add-tags'; export const contextMenuID_Spamfilter = 'mzta-spamfilter'; -export const contextMenuID_Summary = 'mzta-summary'; +export const contextMenuID_Summarize = 'mzta-summarize'; export const contextMenuIconsPath = { [contextMenuID_AddTags]: 'moz-extension:images/autotags.png', [contextMenuID_Spamfilter]: 'moz-extension:images/spamfilter.png', - // [contextMenuID_Summary]: 'moz-extension:images/summary.png', + // [contextMenuID_Summarize]: 'moz-extension:images/summarize.png', }; export function getLanguageDisplayName(languageCode) { diff --git a/mzta-background.js b/mzta-background.js index ce3d1d7b..5ceb483e 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -45,7 +45,7 @@ import { extractJsonObject, contextMenuID_AddTags, contextMenuID_Spamfilter, - contextMenuID_Summary, + contextMenuID_Summarize, contextMenuIconsPath, sanitizeChatGPTModelData, sanitizeChatGPTWebCustomData, @@ -990,10 +990,10 @@ function addContextMenuItems() { addContextMenu(contextMenuID_Spamfilter); } - // Add Context menu: Summary + // Add Context menu: Summarize if(prefs_init.summarize && prefs_init.summarize_context_menu && checkAPIIntegration(prefs_init.connection_type && prefs_init.summarize_use_specific_integration, prefs_init.summarize_connection_type)) { - console.log("adding summary to context menu") - addContextMenu(contextMenuID_Summary); + console.log("adding summarize to context menu") + addContextMenu(contextMenuID_Summariz); } } diff --git a/options/mzta-options.js b/options/mzta-options.js index 16b130fd..3b2ac351 100644 --- a/options/mzta-options.js +++ b/options/mzta-options.js @@ -291,6 +291,10 @@ document.addEventListener('DOMContentLoaded', async () => { document.getElementById('btnManageSpamFilterInfo').addEventListener('click', () => { openTab('/pages/spamfilter/mzta-spamfilter.html'); }); + + document.getElementById('btnManageSummarizeInfo').addEventListener('click', () => { + openTab('/pages/summarize/mzta-summarize.html'); + }); document.getElementById('btnManageCalendarEventInfo').addEventListener('click', () => { openTab('/pages/get-calendar-event/mzta-get-calendar-event.html'); diff --git a/pages/summarize/mzta-summarize.css b/pages/summarize/mzta-summarize.css new file mode 100644 index 00000000..173353e7 --- /dev/null +++ b/pages/summarize/mzta-summarize.css @@ -0,0 +1,123 @@ +#summarize_prompt_container { + width: 90%; + margin: 20px auto; +} + +#summarize_prompt_text { + width: 100%; +} + +.infoline { + font-size: 0.8em; + font-style: italic; +} + +.btn_div { + width: 100%; + display: flex; + justify-content: space-between; +} + +table#miczPrefs { + padding: 10px; + border-spacing: 0px; + width: 90%; + margin: 0px auto 40px auto; + border: 1px solid #ccc; +} + +.section_title { + font-weight: bold; +} + +table#miczPrefs td { + border-top: 1px solid #ccc; + border-bottom: 1px solid #ccc; + vertical-align: top; + padding: 2px; +} + +table#miczPrefs tr:first-child td { + border-top: none; +} + +table#miczPrefs tr:last-child td { + border-bottom: none; +} + +.autocomplete-container { + position: relative; +} + +.autocomplete-list { + position: absolute; + top: 100%; + left: 0; + right: 0; + background-color: white; + border: 1px solid #ccc; + z-index: 1000; + max-height: 200px; + overflow-y: auto; + padding: 0; + margin: 0; + list-style: none; + font-size: small; +} + +.autocomplete-list li { + padding: 8px; + cursor: pointer; +} + +.autocomplete-list li:hover { + background-color: #f0f0f0; +} + +.autocomplete-list li.active { + background-color: #ddd; +} + +.hidden { + display: none; +} + +.unsaved { + color: red; +} + +@media (prefers-color-scheme: dark) { + body { + background-color: #1c1b22; + color: rgb(251, 251, 254); + } + + a:link { + color: #409eff; + } + a:visited { + color: #409eff; + } + a:hover { + color: #66b1ff; + } + a:active { + color: #66b1ff; + } + a:active { + color: #66b1ff; + } + + .autocomplete-list { + background-color: #2e2f36; + border: 1px solid #2e2f36; + } + + .autocomplete-list li:hover { + background-color: #4c4e58; + } + + .autocomplete-list li.active { + background-color: #4c4e58; + } +} diff --git a/pages/summarize/mzta-summarize.html b/pages/summarize/mzta-summarize.html new file mode 100644 index 00000000..7433ec82 --- /dev/null +++ b/pages/summarize/mzta-summarize.html @@ -0,0 +1,30 @@ + + + + + ThunderAI - __MSG_Summarize_PageTitle__ + + + + +
+

__MSG_Summarize_PageTitle__

+

__MSG_Summarize_info_default__

+
+
+ __MSG_Summarize_prompt_text_title__ + +
__MSG_prefs_OptionText_btnManagePrompts_infoline__ __MSG_more_info_string__ +
__MSG_prefs_OptionText_AdvancedPromptResponse_infoline2__
+
+
+ + +
+
+
+
+ + + + diff --git a/pages/summarize/mzta-summarize.js b/pages/summarize/mzta-summarize.js new file mode 100644 index 00000000..646cb096 --- /dev/null +++ b/pages/summarize/mzta-summarize.js @@ -0,0 +1,77 @@ +/* + * ThunderAI [https://micz.it/thunderbird-addon-thunderai/] + * Copyright (C) 2024 - 2025 Mic (m@micz.it) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import { prefs_default } from "../../options/mzta-options-default.js"; +import { taLogger } from "../../js/mzta-logger.js"; +import { getSpecialPrompts, setSpecialPrompts } from "../../js/mzta-prompts.js"; +import { getPlaceholders } from "../../js/mzta-placeholders.js"; +import { textareaAutocomplete } from "../../js/mzta-placeholders-autocomplete.js"; +import { isAPIKeyValue } from "../../js/mzta-utils.js"; + +let autocompleteSuggestions = []; +let taLog = new taLogger("mzta-summarize-page", true); + +document.addEventListener("DOMContentLoaded", async () => { + + i18n.updateDocument(); + + + const summarize_textarea = document.getElementById("summarize_prompt_text"); + const summarize_save_btn = document.getElementById("btn_save_prompt"); + const summarize_reset_btn = document.getElementById("btn_reset_prompt"); + + let specialPrompts = await getSpecialPrompts(); + let summarize_prompt = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize'); + + summarize_textarea.addEventListener("input", (event) => { + summarize_reset_btn.disabled = (event.target.value === browser.i18n.getMessage('prompt_summarize_full_text')); + summarize_save_btn.disabled = (event.target.value === summarize_prompt.text); + if (summarize_save_btn.disabled) { + document.getElementById("summarize_prompt_unsaved").classList.add("hidden"); + } else { + document.getElementById("summarize_prompt_unsaved").classList.remove("hidden"); + } + }); + + summarize_reset_btn.addEventListener("click", () => { + summarize_textarea.value = browser.i18n.getMessage("prompt_summarize_full_text"); + summarize_reset_btn.disabled = true; + let event = new Event("input", { bubbles: true, cancelable: true }); + summarize_textarea.dispatchEvent(event); + }); + + summarize_save_btn.addEventListener("click", () => { + specialPrompts.find(prompt => prompt.id === 'prompt_summarize') + setSpecialPrompts(specialPrompts); + summarize_save_btn.disabled = true; + document.getElementById("summarize_prompt_unsaved").classList.add("hidden"); + browser.runtime.sendMessage({ command: "reload_menus" }); + }); + + if(summarize_prompt.text === 'prompt_summarize_full_text'){ + summarize_prompt.text = browser.i18n.getMessage(summarize_prompt.text); + } + summarize_textarea.value = summarize_prompt.text; + summarize_reset_btn.disabled = (summarize_textarea.value === browser.i18n.getMessage("prompt_summarize_full_text")); + + autocompleteSuggestions = (await getPlaceholders(true)) + .filter((p) => p.id !== "additional_text") + .map((p) => ({ command: `{%${p.id}%}`, type: p.type })); + textareaAutocomplete(summarize_textarea, autocompleteSuggestions, 1); +}); + diff --git a/pages/summary/mzta-summary.css b/pages/summary/mzta-summary.css deleted file mode 100644 index e69de29b..00000000 diff --git a/pages/summary/mzta-summary.html b/pages/summary/mzta-summary.html deleted file mode 100644 index e69de29b..00000000 diff --git a/pages/summary/mzta-summary.js b/pages/summary/mzta-summary.js deleted file mode 100644 index e69de29b..00000000 From 02787e2f25584c736b93efbbfeac58b753aa1197 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Fri, 19 Dec 2025 17:41:36 +0100 Subject: [PATCH 04/26] Fix context menu ID typos --- mzta-background.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mzta-background.js b/mzta-background.js index 5ceb483e..4ef33580 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -923,10 +923,10 @@ function setupStorageChangeListener() { if (changes.summarize) { if (changes.summarize.newValue){ if (prefs_init.summarize_context_menu){ - addContextMenu(contextMenuID_Summary); + addContextMenu(contextMenuID_Summarize); } } else { - removeContextMenu(contextMenuID_Summary); + removeContextMenu(contextMenuID_Summarize); } } reload_pref_init(); @@ -993,7 +993,7 @@ function addContextMenuItems() { // Add Context menu: Summarize if(prefs_init.summarize && prefs_init.summarize_context_menu && checkAPIIntegration(prefs_init.connection_type && prefs_init.summarize_use_specific_integration, prefs_init.summarize_connection_type)) { console.log("adding summarize to context menu") - addContextMenu(contextMenuID_Summariz); + addContextMenu(contextMenuID_Summarize); } } From 8abb7e349bdf53db81452307a0a6e4eee8b15f8b Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Fri, 19 Dec 2025 17:50:31 +0100 Subject: [PATCH 05/26] Add summarize prompt to specialPrompts --- js/mzta-prompts.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/js/mzta-prompts.js b/js/mzta-prompts.js index b598cbce..4ea5d552 100644 --- a/js/mzta-prompts.js +++ b/js/mzta-prompts.js @@ -324,6 +324,22 @@ const specialPrompts = [ is_default: "1", is_special: "1", }, + { + id: 'prompt_summarize', + name: "__MSG_prompt_summarize__", + text: "prompt_summarize_full_text", + type: "1", + action: "0", + need_selected: "0", + need_signature: "0", + need_custom_text: "0", + define_response_lang: "0", + use_diff_viewer: "0", + api_type: '', + api_model: '', + is_default: "1", + is_special: "1", + } ]; From 2c07aa6336cb9aa58cd7dcda78210f8c236f47a6 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Fri, 19 Dec 2025 17:51:24 +0100 Subject: [PATCH 06/26] Remove unused imports from mzta-summarize.js --- pages/summarize/mzta-summarize.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pages/summarize/mzta-summarize.js b/pages/summarize/mzta-summarize.js index 646cb096..4eb831c0 100644 --- a/pages/summarize/mzta-summarize.js +++ b/pages/summarize/mzta-summarize.js @@ -16,12 +16,10 @@ * along with this program. If not, see . */ -import { prefs_default } from "../../options/mzta-options-default.js"; import { taLogger } from "../../js/mzta-logger.js"; import { getSpecialPrompts, setSpecialPrompts } from "../../js/mzta-prompts.js"; import { getPlaceholders } from "../../js/mzta-placeholders.js"; import { textareaAutocomplete } from "../../js/mzta-placeholders-autocomplete.js"; -import { isAPIKeyValue } from "../../js/mzta-utils.js"; let autocompleteSuggestions = []; let taLog = new taLogger("mzta-summarize-page", true); @@ -37,7 +35,7 @@ document.addEventListener("DOMContentLoaded", async () => { let specialPrompts = await getSpecialPrompts(); let summarize_prompt = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize'); - + summarize_textarea.addEventListener("input", (event) => { summarize_reset_btn.disabled = (event.target.value === browser.i18n.getMessage('prompt_summarize_full_text')); summarize_save_btn.disabled = (event.target.value === summarize_prompt.text); From 7eb9a2cf56e685e6dbbafb79d4f4651cfea438ef Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Fri, 19 Dec 2025 18:06:45 +0100 Subject: [PATCH 07/26] fixes saving of summarize prompt --- pages/summarize/mzta-summarize.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/summarize/mzta-summarize.js b/pages/summarize/mzta-summarize.js index 4eb831c0..93bb67e8 100644 --- a/pages/summarize/mzta-summarize.js +++ b/pages/summarize/mzta-summarize.js @@ -54,7 +54,7 @@ document.addEventListener("DOMContentLoaded", async () => { }); summarize_save_btn.addEventListener("click", () => { - specialPrompts.find(prompt => prompt.id === 'prompt_summarize') + specialPrompts.find(prompt => prompt.id === 'prompt_summarize').text = summarize_textarea.value; setSpecialPrompts(specialPrompts); summarize_save_btn.disabled = true; document.getElementById("summarize_prompt_unsaved").classList.add("hidden"); From fe427c29eec5c1e55a39562428b678136e259916 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Tue, 23 Dec 2025 13:22:47 +0100 Subject: [PATCH 08/26] Add Email Template Editor to Summarize Panel --- _locales/en/messages.json | 10 +++++- js/mzta-prompts.js | 16 +++++++++ pages/summarize/mzta-summarize.css | 4 +++ pages/summarize/mzta-summarize.html | 51 ++++++++++++++++++++++++++--- pages/summarize/mzta-summarize.js | 42 +++++++++++++++++++----- 5 files changed, 108 insertions(+), 15 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index c98079bd..c7ceded5 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1008,7 +1008,11 @@ "description": "" }, "prompt_summarize_full_text": { - "message": "Summarize the following email or emails into a bullet point list. If there are multiple emails, consider the entire thread for the summary.", + "message": "Summarize the following email or emails into a bullet point list. If there are multiple emails, consider the entire thread for the summary.\n------------------\n\n", + "description": "" + }, + "prompt_summarize_email_template": { + "message": "From: {%author%}\nTo: {%recipients%}\nCC: {%cc_list%}\nSubject: {%mail_subject%}\nDate: {%mail_datetime%}\nAttachments:\n{%mail_attachments_info%}\nBody:\n{%mail_text_body%}\n---------------\n\n", "description": "" }, "prompt_get_task": { @@ -1075,6 +1079,10 @@ "message": "You can change the prompt as you wish, but the response received from the AI must be in JSON format as specified in the default prompt!", "description": "" }, + "prefs_OptionText_Summarize_infoline2": { + "message": "You can change the prompt as you wish, the first field is the main prompt, the second field is the template for a single mail. The list of emails will be appended to the main prompt.", + "description": "" + }, "prefs_OptionText_get_calendar_event_Sparks_not_present": { "message": "To use the calendar event and task features, please install the ThunderAI Sparks addon.", "description": "" diff --git a/js/mzta-prompts.js b/js/mzta-prompts.js index 4ea5d552..f68a3997 100644 --- a/js/mzta-prompts.js +++ b/js/mzta-prompts.js @@ -339,6 +339,22 @@ const specialPrompts = [ api_model: '', is_default: "1", is_special: "1", + }, + { + id: 'prompt_summarize_email_template', + name: "__MSG_prompt_summarize_email_template__", + text: "prompt_summarize_email_template_full_text", + type: "1", + action: "0", + need_selected: "0", + need_signature: "0", + need_custom_text: "0", + define_response_lang: "0", + use_diff_viewer: "0", + api_type: '', + api_model: '', + is_default: "1", + is_special: "1", } ]; diff --git a/pages/summarize/mzta-summarize.css b/pages/summarize/mzta-summarize.css index 173353e7..f4bbc091 100644 --- a/pages/summarize/mzta-summarize.css +++ b/pages/summarize/mzta-summarize.css @@ -3,6 +3,10 @@ margin: 20px auto; } +#summarize_email_template_text { + width: 100%; +} + #summarize_prompt_text { width: 100%; } diff --git a/pages/summarize/mzta-summarize.html b/pages/summarize/mzta-summarize.html index 7433ec82..54405840 100644 --- a/pages/summarize/mzta-summarize.html +++ b/pages/summarize/mzta-summarize.html @@ -12,17 +12,58 @@

__MSG_Summarize_info_default__

- __MSG_Summarize_prompt_text_title__ - -
__MSG_prefs_OptionText_btnManagePrompts_infoline__ __MSG_more_info_string__ -
__MSG_prefs_OptionText_AdvancedPromptResponse_infoline2__
+ + + __MSG_Summarize_prompt_text_title__ + +
+ + + __MSG_prefs_OptionText_btnManagePrompts_infoline__ + + __MSG_more_info_string__ + + +
+ + __MSG_prefs_OptionText_Summarize_infoline2__ +
+ +
+ +
+
-
+
+ + +
+ + +
+ + +
+ +
+
+ + +
+
diff --git a/pages/summarize/mzta-summarize.js b/pages/summarize/mzta-summarize.js index 93bb67e8..45f7463f 100644 --- a/pages/summarize/mzta-summarize.js +++ b/pages/summarize/mzta-summarize.js @@ -32,41 +32,65 @@ document.addEventListener("DOMContentLoaded", async () => { const summarize_textarea = document.getElementById("summarize_prompt_text"); const summarize_save_btn = document.getElementById("btn_save_prompt"); const summarize_reset_btn = document.getElementById("btn_reset_prompt"); + const summarize_textarea_email_template = document.getElementById("summarize_email_template_text"); + const summarize_reset_email_template_btn = document.getElementById("btn_reset_email_template"); + const summarize_save_email_template_btn = document.getElementById("btn_save_email_template"); let specialPrompts = await getSpecialPrompts(); let summarize_prompt = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize'); + let summarize_email_template = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize_email_template'); summarize_textarea.addEventListener("input", (event) => { summarize_reset_btn.disabled = (event.target.value === browser.i18n.getMessage('prompt_summarize_full_text')); summarize_save_btn.disabled = (event.target.value === summarize_prompt.text); - if (summarize_save_btn.disabled) { - document.getElementById("summarize_prompt_unsaved").classList.add("hidden"); - } else { - document.getElementById("summarize_prompt_unsaved").classList.remove("hidden"); - } }); - + + summarize_textarea_email_template.addEventListener("input", (event) => { + summarize_reset_email_template_btn.disabled = (event.target.value === browser.i18n.getMessage('prompt_summarize_email_template')); + summarize_save_email_template_btn.disabled = (event.target.value === summarize_email_template.text); + }); + + summarize_reset_email_template_btn.addEventListener("click", () => { + summarize_textarea_email_template.value = browser.i18n.getMessage("prompt_summarize_email_template"); + summarize_reset_email_template_btn.disabled = true; + let event = new Event("input", { bubbles: true, cancelable: true }); + summarize_textarea_email_template.dispatchEvent(event); + }); + summarize_reset_btn.addEventListener("click", () => { summarize_textarea.value = browser.i18n.getMessage("prompt_summarize_full_text"); summarize_reset_btn.disabled = true; let event = new Event("input", { bubbles: true, cancelable: true }); summarize_textarea.dispatchEvent(event); }); - + + summarize_save_email_template_btn.addEventListener("click", () => { + specialPrompts.find(prompt => prompt.id === 'prompt_summarize_email_template').text = summarize_textarea_email_template.value; + setSpecialPrompts(specialPrompts); + summarize_save_email_template_btn.disabled = true; + browser.runtime.sendMessage({ command: "reload_menus" }); + }); + summarize_save_btn.addEventListener("click", () => { specialPrompts.find(prompt => prompt.id === 'prompt_summarize').text = summarize_textarea.value; setSpecialPrompts(specialPrompts); summarize_save_btn.disabled = true; - document.getElementById("summarize_prompt_unsaved").classList.add("hidden"); browser.runtime.sendMessage({ command: "reload_menus" }); }); if(summarize_prompt.text === 'prompt_summarize_full_text'){ summarize_prompt.text = browser.i18n.getMessage(summarize_prompt.text); } + if(summarize_email_template === 'prompt_summarize_email_template'){ + summarize_email_template.text = browser.i18n.getMessage(summarize_email_template.text); + } + + summarize_textarea_email_template.value = summarize_email_template.text; + summarize_reset_email_template_btn.disabled = (summarize_email_template.value === browser.i18n.getMessage("prompt_summarize_email_template")); + summarize_textarea.value = summarize_prompt.text; summarize_reset_btn.disabled = (summarize_textarea.value === browser.i18n.getMessage("prompt_summarize_full_text")); - + autocompleteSuggestions = (await getPlaceholders(true)) .filter((p) => p.id !== "additional_text") .map((p) => ({ command: `{%${p.id}%}`, type: p.type })); From f29d8337adeaca64b9df4ed8b76c0ea347a6ad97 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Tue, 23 Dec 2025 13:23:09 +0100 Subject: [PATCH 09/26] WIP: Add Summarize context menu action and handler --- mzta-background.js | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/mzta-background.js b/mzta-background.js index 4ef33580..8d1659ab 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -1003,17 +1003,70 @@ addContextMenuItems(); browser.menus.onClicked.addListener( (info, tab) => { let _add_tags = false let _spamfilter = false + let _summarize = false; if(info.menuItemId === contextMenuID_AddTags){ _add_tags = true; } if(info.menuItemId === contextMenuID_Spamfilter){ _spamfilter = true; } + if(info.menuItemId === contextMenuID_Summarize) { + _summarize = true; + } if(_add_tags || _spamfilter){ processEmails(getMessages(info.selectedMessages), _add_tags, _spamfilter); } + if(_summarize) { + summarizeEmails(getMessages(info.selectedMessages)); + } }); +async function summarizeEmails(messages) { + taWorkingStatus.startWorking(); + // generate the special prompt for summarization + + // - make a long string of all the emails + let emails_content = ''; + for await (let message of messages) { +////// TODO: make this customizable through placeholders! +// plan: make a second option field where placeholders can be used and +// simply add the result from that tepmlate to the end of the prompt option +// field + emails_content += "\n\n---\n\n"; + let curr_fullMessage = await browser.messages.getFull(message.id); + + // from + curr_fullMessage + + // to + // cc + // bcc + // subject + // date + // attachment info + + // body + let msg_text = getMailBody(curr_fullMessage) + let body_text = htmlBodyToPlainText(msg_text.html); + + if( body_text.length == 0 ){ + taLog.log("No HTML found in the message body, using plain text..."); + body_text = msg_text.text.replace(/\s+/g, ' ').trim(); + }; + emails_content += body_text; + + }; + emails_content += "\n\n---\n\n"; + + // - join prompt and emails + + taWorkingStatus.stopWorking(); + // send to LLM + + console.log(emails_content); + +} + // Listening for new received emails const newEmailListener = (folder, messagesList) => { From 7595c70a3834fc97a0139f30026f8d03bbeddaee Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Fri, 2 Jan 2026 20:39:35 +0100 Subject: [PATCH 10/26] Implement summarization internals --- mzta-background.js | 92 ++++++++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 40 deletions(-) diff --git a/mzta-background.js b/mzta-background.js index 8d1659ab..5b92c76c 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -58,7 +58,10 @@ import { } from './js/mzta-utils.js'; import { taPromptUtils } from './js/mzta-utils-prompt.js'; import { mzta_specialCommand } from './js/mzta-special-commands.js'; -import { getSpamFilterPrompt } from './js/mzta-prompts.js'; +import { + getSpamFilterPrompt, + getSpecialPrompts +} from './js/mzta-prompts.js'; import { taSpamReport } from './js/mzta-spamreport.js'; import { taWorkingStatus } from './js/mzta-working-status.js'; import { addTags_getExclusionList, checkExcludedTag } from './js/mzta-addatags-exclusion-list.js'; @@ -1017,54 +1020,63 @@ browser.menus.onClicked.addListener( (info, tab) => { processEmails(getMessages(info.selectedMessages), _add_tags, _spamfilter); } if(_summarize) { + // info.selectedMessages is of type MessageList summarizeEmails(getMessages(info.selectedMessages)); } }); async function summarizeEmails(messages) { taWorkingStatus.startWorking(); - // generate the special prompt for summarization - - // - make a long string of all the emails - let emails_content = ''; - for await (let message of messages) { -////// TODO: make this customizable through placeholders! -// plan: make a second option field where placeholders can be used and -// simply add the result from that tepmlate to the end of the prompt option -// field - emails_content += "\n\n---\n\n"; - let curr_fullMessage = await browser.messages.getFull(message.id); - - // from - curr_fullMessage - - // to - // cc - // bcc - // subject - // date - // attachment info - - // body - let msg_text = getMailBody(curr_fullMessage) - let body_text = htmlBodyToPlainText(msg_text.html); - - if( body_text.length == 0 ){ - taLog.log("No HTML found in the message body, using plain text..."); - body_text = msg_text.text.replace(/\s+/g, ' ').trim(); - }; - emails_content += body_text; - - }; - emails_content += "\n\n---\n\n"; - // - join prompt and emails + // we have two prompts, the actual assignment for the LLM and the email + // template prompt. + const specialPrompts = await getSpecialPrompts(); + const prompt = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize'); + const prompt_email = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize_email_template'); + + const tabs = await browser.tabs.query({ active: true, currentWindow: true }); + const chatgpt_lang = await taPromptUtils.getDefaultLang(prompt); + + // assemble all email messages into one string and add the assignment prompt + let messages_list = []; + for await (let curr_message of messages) { + + // extract body of current message as text + let curr_message_full = await browser.messages.getFull(curr_message.id); + let 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) { + taLog.log("No HTML found in the message body, using plain text..."); + curr_body_full_text = curr_message_full.text; + } + + messages_list.push(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, + })); + }; + let messages_string = messages_list.join("\n\n--- Next Email ---\n\n"); + + let full_prompt = prompt.text + "\n\n\n" + messages_string; + + console.log(full_prompt); + + // send the prompt to the chat interface + openChatGPT( + full_prompt, + prompt.action, + tabs[0].id, + prompt.name, + prompt.need_custom_text, + prompt + ) taWorkingStatus.stopWorking(); - // send to LLM - - console.log(emails_content); - + return {ok : '1'}; } From c3e27f03564703ce5344faa6c3ec9fe32932bc30 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Fri, 2 Jan 2026 21:42:44 +0100 Subject: [PATCH 11/26] Fix malformed label closing tag in options page --- options/mzta-options.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options/mzta-options.html b/options/mzta-options.html index 99078369..02b0f6ea 100644 --- a/options/mzta-options.html +++ b/options/mzta-options.html @@ -175,7 +175,7 @@ From 3d2c2b93208771187f6ad93882a5efe477f076d5 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Sat, 3 Jan 2026 22:18:00 +0100 Subject: [PATCH 12/26] add customizable model options for summarize --- _locales/en/messages.json | 8 ++ pages/summarize/mzta-summarize.css | 92 +++++++++++--- pages/summarize/mzta-summarize.html | 24 +++- pages/summarize/mzta-summarize.js | 181 +++++++++++++++++++++++++--- 4 files changed, 270 insertions(+), 35 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index c7ceded5..a7a0102d 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1003,6 +1003,10 @@ "message": "Extract all relevant details required to generate a calendar event from the following text. The extracted information should include:\n- Event Title\n- Start Date and Time (including timezone, if specified)\n- End Date and Time (including timezone, if specified)\n- Full day (if mentioned)\n- Attendees\nEnsure the data is formatted clearly and consistently so that it can be directly used for creating a calendar event.\nIf there are relative time references, consider that the date and time of the email are \"{%mail_datetime%}\". Calculate the start date and time based on this reference. If the calculated start date and time are earlier than \"{%current_datetime%}\", recalculate the start date and time using \"{%current_datetime%}\" as the base.\nIf the duration is not specified, set it to one hour.\nThese are the attendees: {%author%}, {%recipients%}, {%cc_list%}. If present, exclude my address: {%account_email_address%}.\nIf you're not able to get one or more of the required information, please respond with an empty string.\nGenerate a response in JSON format only. Do not include any additional text or explanations; provide only the JSON. Here is the format to be used:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"Calendar event summary here\",\n\"forceAllDay\": false,\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nHere's the text:\"{%selected_text%}\"", "description": "" }, + "Summarize_prompt_prefs_title": { + "message": "Add Summarization Options", + "description": "" + }, "prompt_summarize": { "message": "Summarize this email or these emails", "description": "" @@ -1215,6 +1219,10 @@ "message": "Summarize mail", "description": "" }, + "prefs_OptionText_summarize_use_specific_integration_Info": { + "message": "If checked, the Model and API specified below will be used for summarizing email(s), regardless the one choosen in the ThunderAI options page.", + "description": "" + }, "prefs_OptionText_summarize_Info": { "message": "If checked, adds an option to context menu to summarize mail.", "description": "" diff --git a/pages/summarize/mzta-summarize.css b/pages/summarize/mzta-summarize.css index f4bbc091..26b08e10 100644 --- a/pages/summarize/mzta-summarize.css +++ b/pages/summarize/mzta-summarize.css @@ -16,6 +16,49 @@ font-style: italic; } +#account_selector_container{ + display: none; +} + +#account_selector_checkboxes{ + padding: 10px; + border: 1px solid gray; + width: 24em; + margin-bottom: 10px; + margin-top: 10px; +} + +.specific_integration{ + background-color: #dfeaff; +} + +table { + border-collapse: separate; + border-spacing: 0; +} + +.group td { + border-top: none; + border-bottom: none; +} + +.specific_integration_sub td:first-child { + border-left: 10px solid #dfeaff; +} + +.specific_integration_sub td:last-child { + border-right: 10px solid #dfeaff; +} + +#connection_ui_end td{ + height: 6px; + background-color: #dfeaff; +} + +#connection_ui_end{ + display: none; +} + .btn_div { width: 100%; display: flex; @@ -30,6 +73,11 @@ table#miczPrefs { border: 1px solid #ccc; } +.addtags_table_title{ + width: 90%; + margin: auto; +} + .section_title { font-weight: bold; } @@ -90,27 +138,21 @@ table#miczPrefs tr:last-child td { color: red; } +label:has(input[type="checkbox"]) { + cursor: pointer; +} + @media (prefers-color-scheme: dark) { body { background-color: #1c1b22; color: rgb(251, 251, 254); } - a:link { - color: #409eff; - } - a:visited { - color: #409eff; - } - a:hover { - color: #66b1ff; - } - a:active { - color: #66b1ff; - } - a:active { - color: #66b1ff; - } + a:link { color: #409eff; } + a:visited { color: #409eff; } + a:hover { color: #66b1ff; } + a:active { color: #66b1ff; } + a:active { color: #66b1ff; } .autocomplete-list { background-color: #2e2f36; @@ -124,4 +166,24 @@ table#miczPrefs tr:last-child td { .autocomplete-list li.active { background-color: #4c4e58; } + + .summarize_auto{ + background-color: #415c35; + } + + .specific_integration{ + background-color: #2E3A4F; + } + + .specific_integration_sub td:first-child { + border-left: 10px solid #2E3A4F; + } + + .specific_integration_sub td:last-child { + border-right: 10px solid #2E3A4F; + } + + #connection_ui_end td{ + background-color: #2E3A4F; + } } diff --git a/pages/summarize/mzta-summarize.html b/pages/summarize/mzta-summarize.html index 54405840..82679339 100644 --- a/pages/summarize/mzta-summarize.html +++ b/pages/summarize/mzta-summarize.html @@ -4,13 +4,29 @@ ThunderAI - __MSG_Summarize_PageTitle__ + -
-

__MSG_Summarize_PageTitle__

-

__MSG_Summarize_info_default__

-
+
+

__MSG_Summarize_PageTitle__

+

__MSG_Summarize_info_default__

+
+
__MSG_Summarize_prompt_prefs_title__
+ + + + + + + +
__MSG_prefs_OptionText_use_specific_integration__ + + +
diff --git a/pages/summarize/mzta-summarize.js b/pages/summarize/mzta-summarize.js index 45f7463f..20252fc4 100644 --- a/pages/summarize/mzta-summarize.js +++ b/pages/summarize/mzta-summarize.js @@ -16,29 +16,71 @@ * along with this program. If not, see . */ +import { prefs_default, integration_options_config } from '../../options/mzta-options-default.js'; import { taLogger } from "../../js/mzta-logger.js"; -import { getSpecialPrompts, setSpecialPrompts } from "../../js/mzta-prompts.js"; -import { getPlaceholders } from "../../js/mzta-placeholders.js"; +import { + getSpecialPrompts, + setSpecialPrompts +} from "../../js/mzta-prompts.js"; +import { + getPlaceholders, + mapPlaceholderToSuggestion +} from "../../js/mzta-placeholders.js"; import { textareaAutocomplete } from "../../js/mzta-placeholders-autocomplete.js"; +import { + getAccountsList, + normalizeStringList, + isAPIKeyValue +} from "../../js/mzta-utils.js"; +import { + initializeSpecificIntegrationUI +} from "../_lib/connection-ui.js"; let autocompleteSuggestions = []; let taLog = new taLogger("mzta-summarize-page", true); document.addEventListener("DOMContentLoaded", async () => { - i18n.updateDocument(); - - - const summarize_textarea = document.getElementById("summarize_prompt_text"); - const summarize_save_btn = document.getElementById("btn_save_prompt"); - const summarize_reset_btn = document.getElementById("btn_reset_prompt"); - const summarize_textarea_email_template = document.getElementById("summarize_email_template_text"); - const summarize_reset_email_template_btn = document.getElementById("btn_reset_email_template"); - const summarize_save_email_template_btn = document.getElementById("btn_save_email_template"); - let specialPrompts = await getSpecialPrompts(); let summarize_prompt = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize'); let summarize_email_template = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize_email_template'); + + if (summarize_prompt && summarize_prompt.api_type && summarize_prompt.api_type !== '') { + let update_prefs = {}; + update_prefs['summarize_connection_type'] = summarize_prompt.api_type; + + let integration = summarize_prompt.api_type.replace('_api', ''); + if (integration_options_config && integration_options_config[integration]) { + for (const key of Object.keys(integration_options_config[integration])) { + if (summarize_prompt[key] !== undefined) { + update_prefs[`summarize_${integration}_${key}`] = summarize_prompt[key]; + } + } + } + await browser.storage.sync.set(update_prefs); + } + + await initializeSpecificIntegrationUI({ + prefix: 'summarize', + promptId: 'prompt_summarize', + taLog: taLog, + restoreOptionsCallback: restoreOptions + }); + + i18n.updateDocument(); + + document.querySelectorAll(".option-input").forEach(element => { + element.addEventListener("change", saveOptions); + }); + let prefs_summarize = await browser.storage.sync.get({ summarize_enabled_accounts: [], connection_type: 'chatgpt_web' }); + + let summarize_textarea = document.getElementById("summarize_prompt_text"); + let summarize_save_btn = document.getElementById("btn_save_prompt"); + let summarize_reset_btn = document.getElementById("btn_reset_prompt"); + let summarize_textarea_email_template = document.getElementById("summarize_email_template_text"); + let summarize_reset_email_template_btn = document.getElementById("btn_reset_email_template"); + let summarize_save_email_template_btn = document.getElementById("btn_save_email_template"); + summarize_textarea.addEventListener("input", (event) => { summarize_reset_btn.disabled = (event.target.value === browser.i18n.getMessage('prompt_summarize_full_text')); @@ -84,16 +126,123 @@ document.addEventListener("DOMContentLoaded", async () => { if(summarize_email_template === 'prompt_summarize_email_template'){ summarize_email_template.text = browser.i18n.getMessage(summarize_email_template.text); } - summarize_textarea_email_template.value = summarize_email_template.text; summarize_reset_email_template_btn.disabled = (summarize_email_template.value === browser.i18n.getMessage("prompt_summarize_email_template")); - summarize_textarea.value = summarize_prompt.text; summarize_reset_btn.disabled = (summarize_textarea.value === browser.i18n.getMessage("prompt_summarize_full_text")); - + autocompleteSuggestions = (await getPlaceholders(true)) .filter((p) => p.id !== "additional_text") - .map((p) => ({ command: `{%${p.id}%}`, type: p.type })); + .map(mapPlaceholderToSuggestion); textareaAutocomplete(summarize_textarea, autocompleteSuggestions, 1); + textareaAutocomplete(summarize_textarea_email_template, autocompleteSuggestions, 1); + }); +// Methods to manage options, derived from: /options/mzta-options.js + +function saveOptions(e) { + e.preventDefault(); + let options = {}; + let element = e.target; + // console.log(">>>>>>>>>> Saving option: " + element.id + " = " + element.value); + switch (element.type) { + case 'checkbox': + options[element.id] = element.checked; + break; + case 'number': + options[element.id] = element.valueAsNumber; + break; + case 'text': + case 'password': + options[element.id] = element.value.trim(); + break; + case 'select-one': + // console.log(">>>>>>>>>> Saving option [select-one]: " + element.id + " = " + element.value); + options[element.id] = element.value; + break; + case 'textarea': + if(element.id === 'add_tags_auto_uselist_list') { + element.value = normalizeStringList(element.value, 1); + } + options[element.id] = normalizeStringList(element.value); + break; + default: + console.error("[ThunderAI] Unhandled input type:", element.type); + } + + browser.storage.sync.set(options); +} + +async function restoreOptions() { + function setCurrentChoice(result) { + document.querySelectorAll(".option-input").forEach(element => { + taLog.log("Options restoring " + element.id + " = " + (isAPIKeyValue(element.id) ? "****************" : result[element.id])); + switch (element.type) { + case 'checkbox': + element.checked = result[element.id] || false; + break; + case 'number': + let default_number_value = 0; + if(element.id == 'chatgpt_win_height') default_number_value = prefs_default.chatgpt_win_height; + if(element.id == 'chatgpt_win_width') default_number_value = prefs_default.chatgpt_win_width; + element.value = result[element.id] ?? default_number_value; + break; + case 'text': + case 'textarea': + case 'password': + let default_text_value = ''; + if(element.id == 'default_chatgpt_lang') default_text_value = prefs_default.default_chatgpt_lang; + if(element.id === 'add_tags_auto_uselist_list') { + result[element.id] = normalizeStringList(result[element.id], 1); + } + element.value = result[element.id] || default_text_value; + break; + default: + if (element.tagName === 'SELECT') { + let default_select_value = ''; + const restoreValue = result[element.id] || default_select_value; + // Check if option exists + let optionExists = Array.from(element.options).some(opt => opt.value === restoreValue); + // If it doesn't exist and restoreValue is not empty, create it + if (!optionExists && restoreValue !== '') { + let newOption = new Option(restoreValue, restoreValue); + element.add(newOption); + } + // Set value + element.value = restoreValue; + if (element.value === '') { + element.selectedIndex = -1; + } + }else{ + console.error("[ThunderAI] Unhandled input type:", element.type); + } + } + }); + } + + let getting = await browser.storage.sync.get(prefs_default); + + let specialPrompts = await getSpecialPrompts(); + let addtags_prompt = specialPrompts.find(prompt => prompt.id === 'prompt_add_tags'); + + if (addtags_prompt) { + if (addtags_prompt.api_type && addtags_prompt.api_type !== '') { + getting['add_tags_connection_type'] = addtags_prompt.api_type; + } else { + getting['add_tags_connection_type'] = getting['connection_type']; + } + for (const [integration, options] of Object.entries(integration_options_config)) { + for (const key of Object.keys(options)) { + const propName = `${integration}_${key}`; + if (addtags_prompt[propName] !== undefined && addtags_prompt[propName] !== '') { + getting[`add_tags_${propName}`] = addtags_prompt[propName]; + } else { + getting[`add_tags_${propName}`] = getting[propName]; + } + } + } + } + + setCurrentChoice(getting); +} From 66cc35746f7b8299117a8b852c83b7eb56019db7 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Wed, 7 Jan 2026 20:40:15 +0100 Subject: [PATCH 13/26] move email separators to extra prompt field --- _locales/en/messages.json | 8 +++++-- js/mzta-prompts.js | 18 ++++++++++++++- mzta-background.js | 11 +++++---- pages/summarize/mzta-summarize.css | 4 ++++ pages/summarize/mzta-summarize.html | 16 +++++++++++++ pages/summarize/mzta-summarize.js | 36 +++++++++++++++++++++++++++++ 6 files changed, 85 insertions(+), 8 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index a7a0102d..08f5a2f8 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1012,11 +1012,15 @@ "description": "" }, "prompt_summarize_full_text": { - "message": "Summarize the following email or emails into a bullet point list. If there are multiple emails, consider the entire thread for the summary.\n------------------\n\n", + "message": "Summarize the following email or emails into a bullet point list. If there are multiple emails, consider the entire thread for the summary.", "description": "" }, "prompt_summarize_email_template": { - "message": "From: {%author%}\nTo: {%recipients%}\nCC: {%cc_list%}\nSubject: {%mail_subject%}\nDate: {%mail_datetime%}\nAttachments:\n{%mail_attachments_info%}\nBody:\n{%mail_text_body%}\n---------------\n\n", + "message": "From: {%author%}\nTo: {%recipients%}\nCC: {%cc_list%}\nSubject: {%mail_subject%}\nDate: {%mail_datetime%}\nAttachments:\n{%mail_attachments_info%}\nBody:\n{%mail_text_body%}", + "description": "" + }, + "prompt_summarize_email_separator": { + "message": "\n\n---------- NEXT EMAIL ----------\n\n", "description": "" }, "prompt_get_task": { diff --git a/js/mzta-prompts.js b/js/mzta-prompts.js index f68a3997..76736064 100644 --- a/js/mzta-prompts.js +++ b/js/mzta-prompts.js @@ -355,6 +355,22 @@ const specialPrompts = [ api_model: '', is_default: "1", is_special: "1", + }, + { + id: 'prompt_summarize_email_separator', + name: "__MSG_prompt_summarize_email_separator__", + text: "prompt_summarize_email_separator_full_text", + type: "1", + action: "0", + need_selected: "0", + need_signature: "0", + need_custom_text: "0", + define_response_lang: "0", + use_diff_viewer: "0", + api_type: '', + api_model: '', + is_default: "1", + is_special: "1", } ]; @@ -619,4 +635,4 @@ export async function clearPromptAPI(id){ } // console.log(">>>>>>>>>>>>> clearPromptAPI _prompt AFTER: " + JSON.stringify(_prompt)); await savePrompt(_prompt); -} \ No newline at end of file +} diff --git a/mzta-background.js b/mzta-background.js index 5b92c76c..46370b6f 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -1028,11 +1028,12 @@ browser.menus.onClicked.addListener( (info, tab) => { async function summarizeEmails(messages) { taWorkingStatus.startWorking(); - // we have two prompts, the actual assignment for the LLM and the email - // template prompt. + // we have three prompts, the actual assignment for the LLM, the email + // template prompt, and the email separator prompt const specialPrompts = await getSpecialPrompts(); const prompt = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize'); 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); @@ -1059,11 +1060,11 @@ async function summarizeEmails(messages) { msg_text: curr_body_full_html, })); }; - let messages_string = messages_list.join("\n\n--- Next Email ---\n\n"); + const messages_string = messages_list.join(prompt_email_separator_string); - let full_prompt = prompt.text + "\n\n\n" + messages_string; + const full_prompt = prompt_string + prompt_email_separator_string + messages_string; - console.log(full_prompt); + // console.log(full_prompt); // send the prompt to the chat interface openChatGPT( diff --git a/pages/summarize/mzta-summarize.css b/pages/summarize/mzta-summarize.css index 26b08e10..add7a975 100644 --- a/pages/summarize/mzta-summarize.css +++ b/pages/summarize/mzta-summarize.css @@ -11,6 +11,10 @@ width: 100%; } +#summarize_email_separator_text { + width: 100%; +} + .infoline { font-size: 0.8em; font-style: italic; diff --git a/pages/summarize/mzta-summarize.html b/pages/summarize/mzta-summarize.html index 82679339..12c94b3c 100644 --- a/pages/summarize/mzta-summarize.html +++ b/pages/summarize/mzta-summarize.html @@ -79,6 +79,22 @@ __MSG_save__
+ + +
+ + +
+ +
+
+ + +
diff --git a/pages/summarize/mzta-summarize.js b/pages/summarize/mzta-summarize.js index 20252fc4..bd57c0c5 100644 --- a/pages/summarize/mzta-summarize.js +++ b/pages/summarize/mzta-summarize.js @@ -44,6 +44,7 @@ document.addEventListener("DOMContentLoaded", async () => { let specialPrompts = await getSpecialPrompts(); let summarize_prompt = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize'); let summarize_email_template = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize_email_template'); + let summarize_email_separator = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize_email_separator'); if (summarize_prompt && summarize_prompt.api_type && summarize_prompt.api_type !== '') { let update_prefs = {}; @@ -80,8 +81,12 @@ document.addEventListener("DOMContentLoaded", async () => { let summarize_textarea_email_template = document.getElementById("summarize_email_template_text"); let summarize_reset_email_template_btn = document.getElementById("btn_reset_email_template"); let summarize_save_email_template_btn = document.getElementById("btn_save_email_template"); + let summarize_email_separator_textarea = document.getElementById("summarize_email_separator_text"); + let summarize_email_separator_save_btn = document.getElementById("btn_save_email_separator"); + let summarize_email_separator_reset_btn = document.getElementById("btn_reset_email_separator"); + // on changing textareas summarize_textarea.addEventListener("input", (event) => { summarize_reset_btn.disabled = (event.target.value === browser.i18n.getMessage('prompt_summarize_full_text')); summarize_save_btn.disabled = (event.target.value === summarize_prompt.text); @@ -91,7 +96,14 @@ document.addEventListener("DOMContentLoaded", async () => { summarize_reset_email_template_btn.disabled = (event.target.value === browser.i18n.getMessage('prompt_summarize_email_template')); summarize_save_email_template_btn.disabled = (event.target.value === summarize_email_template.text); }); + + summarize_email_separator_textarea.addEventListener("input", (event) => { + summarize_email_separator_reset_btn.disabled = (event.target.value === browser.i18n.getMessage('prompt_summarize_email_separator')); + summarize_email_separator_save_btn.disabled = (event.target.value === summarize_email_separator.text); + }); + + // on clicking buttons, reset summarize_reset_email_template_btn.addEventListener("click", () => { summarize_textarea_email_template.value = browser.i18n.getMessage("prompt_summarize_email_template"); summarize_reset_email_template_btn.disabled = true; @@ -105,7 +117,15 @@ document.addEventListener("DOMContentLoaded", async () => { let event = new Event("input", { bubbles: true, cancelable: true }); summarize_textarea.dispatchEvent(event); }); + + summarize_email_separator_reset_btn.addEventListener("click", () => { + summarize_email_separator_textarea.value = browser.i18n.getMessage("prompt_summarize_email_separator"); + summarize_email_separator_reset_btn.disabled = true; + let event = new Event("input", { bubbles: true, cancelable: true }); + summarize_email_separator_textarea.dispatchEvent(event); + }); + // on clicking buttons, save summarize_save_email_template_btn.addEventListener("click", () => { specialPrompts.find(prompt => prompt.id === 'prompt_summarize_email_template').text = summarize_textarea_email_template.value; setSpecialPrompts(specialPrompts); @@ -119,6 +139,14 @@ document.addEventListener("DOMContentLoaded", async () => { summarize_save_btn.disabled = true; browser.runtime.sendMessage({ command: "reload_menus" }); }); + + summarize_email_separator_save_btn.addEventListener("click", () => { + specialPrompts.find(prompt => prompt.id === 'prompt_summarize_email_separator').text = summarize_email_separator_textarea.value; + setSpecialPrompts(specialPrompts); + summarize_email_separator_save_btn.disabled = true; + browser.runtime.sendMessage({ command: "reload_menus" }); + }); + if(summarize_prompt.text === 'prompt_summarize_full_text'){ summarize_prompt.text = browser.i18n.getMessage(summarize_prompt.text); @@ -126,16 +154,24 @@ document.addEventListener("DOMContentLoaded", async () => { if(summarize_email_template === 'prompt_summarize_email_template'){ summarize_email_template.text = browser.i18n.getMessage(summarize_email_template.text); } + if(summarize_email_separator.text === 'prompt_summarize_email_separator'){ + summarize_email_separator.text = browser.i18n.getMessage(summarize_email_separator.text); + } + summarize_textarea_email_template.value = summarize_email_template.text; summarize_reset_email_template_btn.disabled = (summarize_email_template.value === browser.i18n.getMessage("prompt_summarize_email_template")); summarize_textarea.value = summarize_prompt.text; summarize_reset_btn.disabled = (summarize_textarea.value === browser.i18n.getMessage("prompt_summarize_full_text")); + summarize_email_separator_textarea.value = summarize_email_separator.text; + summarize_email_separator_reset_btn.disabled = (summarize_email_separator.value === browser.i18n.getMessage("prompt_summarize_email_separator")); autocompleteSuggestions = (await getPlaceholders(true)) .filter((p) => p.id !== "additional_text") .map(mapPlaceholderToSuggestion); + textareaAutocomplete(summarize_textarea, autocompleteSuggestions, 1); textareaAutocomplete(summarize_textarea_email_template, autocompleteSuggestions, 1); + textareaAutocomplete(summarize_email_separator_textarea, autocompleteSuggestions, 1); }); From 24edcc09e6875b15fa500ad1c23a5d132b918118 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Wed, 7 Jan 2026 20:40:43 +0100 Subject: [PATCH 14/26] make some variables const --- mzta-background.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mzta-background.js b/mzta-background.js index 46370b6f..40532152 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -1039,13 +1039,13 @@ async function summarizeEmails(messages) { const chatgpt_lang = await taPromptUtils.getDefaultLang(prompt); // assemble all email messages into one string and add the assignment prompt - let messages_list = []; + const messages_list = []; for await (let curr_message of messages) { // extract body of current message as text - let curr_message_full = await browser.messages.getFull(curr_message.id); - let curr_body_full_html = getMailBody(curr_message_full); - let curr_body_full_text = htmlBodyToPlainText(curr_body_full_html.html); + const curr_message_full = await browser.messages.getFull(curr_message.id); + const curr_body_full_html = getMailBody(curr_message_full); + const curr_body_full_text = htmlBodyToPlainText(curr_body_full_html.html); if( curr_body_full_text.length === 0) { taLog.log("No HTML found in the message body, using plain text..."); curr_body_full_text = curr_message_full.text; From c6bf2edba50f53babfbc13b2aba0a788ee30edfa Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Wed, 7 Jan 2026 20:40:58 +0100 Subject: [PATCH 15/26] allow placeholders in other summarize prompts --- mzta-background.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/mzta-background.js b/mzta-background.js index 40532152..c06d5c0b 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -1037,6 +1037,19 @@ async function summarizeEmails(messages) { 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 + // separator prompt do not have a message as context, so there is only + // limited things to replace + 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, + }); + // assemble all email messages into one string and add the assignment prompt const messages_list = []; From c261c1da9049be324977c141c709d082cc10e158 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Wed, 7 Jan 2026 20:41:18 +0100 Subject: [PATCH 16/26] fix bug --- pages/summarize/mzta-summarize.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/summarize/mzta-summarize.js b/pages/summarize/mzta-summarize.js index bd57c0c5..ea8d8364 100644 --- a/pages/summarize/mzta-summarize.js +++ b/pages/summarize/mzta-summarize.js @@ -151,7 +151,7 @@ document.addEventListener("DOMContentLoaded", async () => { if(summarize_prompt.text === 'prompt_summarize_full_text'){ summarize_prompt.text = browser.i18n.getMessage(summarize_prompt.text); } - if(summarize_email_template === 'prompt_summarize_email_template'){ + if(summarize_email_template.text === 'prompt_summarize_email_template'){ summarize_email_template.text = browser.i18n.getMessage(summarize_email_template.text); } if(summarize_email_separator.text === 'prompt_summarize_email_separator'){ From 30a7de3756575fc8c292422dbfd8f5ad4fc3ff95 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Wed, 7 Jan 2026 20:43:42 +0100 Subject: [PATCH 17/26] better explanation --- _locales/en/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 08f5a2f8..f64feb2d 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1088,7 +1088,7 @@ "description": "" }, "prefs_OptionText_Summarize_infoline2": { - "message": "You can change the prompt as you wish, the first field is the main prompt, the second field is the template for a single mail. The list of emails will be appended to the main prompt.", + "message": "You can change the prompt as you wish, the first field is the main prompt, the second field is the template for a single mail. The list of emails will be appended to the main prompt. Emails will be separated by the separator specified in the third field.", "description": "" }, "prefs_OptionText_get_calendar_event_Sparks_not_present": { From d601121852e8ab6095de7d8e3d177afc4067a065 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Sat, 10 Jan 2026 22:59:26 +0100 Subject: [PATCH 18/26] better descriptions for prompts --- _locales/en/messages.json | 12 ++++++++++++ pages/summarize/mzta-summarize.html | 22 +++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index f64feb2d..2d7329ec 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1091,6 +1091,18 @@ "message": "You can change the prompt as you wish, the first field is the main prompt, the second field is the template for a single mail. The list of emails will be appended to the main prompt. Emails will be separated by the separator specified in the third field.", "description": "" }, + "prefs_OptionText_Summarize_main_prompt": { + "message": "The main prompt describing the task to be performed on all selected emails:", + "description": "" + }, + "prefs_OptionText_Summarize_email_template": { + "message": "The template for a single email:", + "description": "" + }, + "prefs_OptionText_Summarize_email_separator": { + "message": "The separator between emails:", + "description": "" + }, "prefs_OptionText_get_calendar_event_Sparks_not_present": { "message": "To use the calendar event and task features, please install the ThunderAI Sparks addon.", "description": "" diff --git a/pages/summarize/mzta-summarize.html b/pages/summarize/mzta-summarize.html index 12c94b3c..a8db111d 100644 --- a/pages/summarize/mzta-summarize.html +++ b/pages/summarize/mzta-summarize.html @@ -27,6 +27,8 @@ + +
@@ -47,8 +49,14 @@
- + + + __MSG_prefs_OptionText_Summarize_main_prompt__ + + +
+
@@ -65,6 +73,12 @@
+ + __MSG_prefs_OptionText_Summarize_email_template__ + + +
+
@@ -81,6 +95,12 @@
+ + __MSG_prefs_OptionText_Summarize_email_separator__ + + +
+
From 51c81418a38e007b9d5ba181a87332bb25217c45 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Sat, 10 Jan 2026 23:00:20 +0100 Subject: [PATCH 19/26] handle disabling summarize options button --- options/mzta-options-default.js | 2 +- options/mzta-options.html | 2 +- options/mzta-options.js | 41 +++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/options/mzta-options-default.js b/options/mzta-options-default.js index e14b2808..6fd52d1d 100644 --- a/options/mzta-options-default.js +++ b/options/mzta-options-default.js @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -const special_prompts_with_integration = ['add_tags', 'spamfilter']; +const special_prompts_with_integration = ['add_tags', 'spamfilter', 'summarize']; export const integration_options_config = { chatgpt: { diff --git a/options/mzta-options.html b/options/mzta-options.html index 02b0f6ea..534f33b8 100644 --- a/options/mzta-options.html +++ b/options/mzta-options.html @@ -154,7 +154,7 @@ " diff --git a/options/mzta-options.js b/options/mzta-options.js index 3b2ac351..46a7439d 100644 --- a/options/mzta-options.js +++ b/options/mzta-options.js @@ -167,6 +167,27 @@ function disable_SpamFilter(prefs_opt){ } } +function disable_Summarize(prefs_opt){ + let summarize = document.getElementById('summarize'); + let conntype_select = document.getElementById("connection_type"); + const tempPrefs = { + connection_type: conntype_select.value, + ...prefs_opt + }; + let summarize_disabled = (getConnectionType(tempPrefs, null, 'summarize') === "chatgpt_web"); + let summarize_checked_original = summarize.checked; + summarize.checked = summarize_disabled ? false : summarize.checked; + if(!summarize.checked){ + let summarize_info_btn = document.getElementById('btnManageSummarizeInfo'); + summarize_info_btn.disabled = 'disabled'; + } + let summarize_warn_API_needed = document.getElementById('summarize_warn_API_needed'); + summarize_warn_API_needed.style.display = (summarize_disabled) ? 'inline-block' : 'none'; + if(summarize_checked_original != summarize.checked){ + browser.storage.sync.set({summarize: summarize.checked}); + } +} + async function disable_GetCalendarEvent(){ let get_calendar_event = document.getElementById('get_calendar_event'); let get_task = document.getElementById('get_task'); @@ -262,6 +283,24 @@ document.addEventListener('DOMContentLoaded', async () => { }); spamfilter_info_btn.disabled = spamfilter_el.checked ? '' : 'disabled'; + let summarize_el = document.getElementById('summarize'); + let summarize_info_btn = document.getElementById('btnManageSummarizeInfo'); + summarize_el.addEventListener('click', (event) => { + async function _summarize_el_change() { + if (event.target.checked) { + let granted = await messenger.permissions.request({ permissions: ["messagesRead"] }); + if (!granted) { + event.target.checked = false; + summarize_info_btn.disabled = 'disabled'; + browser.storage.sync.set({summarize: false}); + } + } + } + _summarize_el_change(); + summarize_info_btn.disabled = event.target.checked ? '' : 'disabled'; + }); + summarize_info_btn.disabled = summarize_el.checked ? '' : 'disabled'; + let get_calendar_event_el = document.getElementById('get_calendar_event'); let get_calendar_event_info_btn = document.getElementById('btnManageCalendarEventInfo'); get_calendar_event_el.addEventListener('click', (event) => { @@ -321,12 +360,14 @@ document.addEventListener('DOMContentLoaded', async () => { conntype_select.addEventListener("change", disable_MaxPromptLength); conntype_select.addEventListener("change", () => disable_AddTags(prefs_opt)); conntype_select.addEventListener("change", () => disable_SpamFilter(prefs_opt)); + conntype_select.addEventListener("change", () => disable_Summarize(prefs_opt)); conntype_select.addEventListener("change", disable_GetCalendarEvent); showConnectionOptions(conntype_select); disable_MaxPromptLength(); disable_AddTags(prefs_opt); disable_SpamFilter(prefs_opt); + disable_Summarize(prefs_opt); disable_GetCalendarEvent(); document.getElementById('reset_max_prompt_length').addEventListener('click', resetMaxPromptLength); From 8679e1cb085b5b3882c1ec7f0b72985ff94a3e84 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Fri, 16 Jan 2026 12:55:56 +0100 Subject: [PATCH 20/26] remove checking for non-optional permission --- options/mzta-options.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/options/mzta-options.js b/options/mzta-options.js index 46a7439d..7563cd51 100644 --- a/options/mzta-options.js +++ b/options/mzta-options.js @@ -286,17 +286,6 @@ document.addEventListener('DOMContentLoaded', async () => { let summarize_el = document.getElementById('summarize'); let summarize_info_btn = document.getElementById('btnManageSummarizeInfo'); summarize_el.addEventListener('click', (event) => { - async function _summarize_el_change() { - if (event.target.checked) { - let granted = await messenger.permissions.request({ permissions: ["messagesRead"] }); - if (!granted) { - event.target.checked = false; - summarize_info_btn.disabled = 'disabled'; - browser.storage.sync.set({summarize: false}); - } - } - } - _summarize_el_change(); summarize_info_btn.disabled = event.target.checked ? '' : 'disabled'; }); summarize_info_btn.disabled = summarize_el.checked ? '' : 'disabled'; From 90dcf8437b4d54a18cb0547e47ef82b4542139a3 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Fri, 16 Jan 2026 12:56:35 +0100 Subject: [PATCH 21/26] remove stray console logging --- mzta-background.js | 1 - 1 file changed, 1 deletion(-) diff --git a/mzta-background.js b/mzta-background.js index c06d5c0b..da862ccb 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -995,7 +995,6 @@ function addContextMenuItems() { // Add Context menu: Summarize if(prefs_init.summarize && prefs_init.summarize_context_menu && checkAPIIntegration(prefs_init.connection_type && prefs_init.summarize_use_specific_integration, prefs_init.summarize_connection_type)) { - console.log("adding summarize to context menu") addContextMenu(contextMenuID_Summarize); } } From f147a0453b8e3781b3fb7b1d77322d74a07edb77 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Fri, 16 Jan 2026 12:57:06 +0100 Subject: [PATCH 22/26] remove summarize_context_menu option in favor of summarize option --- mzta-background.js | 5 ++--- options/mzta-options-default.js | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/mzta-background.js b/mzta-background.js index da862ccb..d433581a 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -811,7 +811,6 @@ async function reload_pref_init(){ dynamic_menu_force_enter: prefs_default.dynamic_menu_force_enter, add_tags_context_menu: prefs_default.add_tags_context_menu, spamfilter_context_menu: prefs_default.spamfilter_context_menu, - summarize_context_menu: prefs_default.summarize_context_menu, ...getDynamicSettingsDefaults(['use_specific_integration', 'connection_type']) }); _process_incoming = prefs_init.add_tags_auto || prefs_init.spamfilter; @@ -925,7 +924,7 @@ function setupStorageChangeListener() { } if (changes.summarize) { if (changes.summarize.newValue){ - if (prefs_init.summarize_context_menu){ + if (prefs_init.summarize){ addContextMenu(contextMenuID_Summarize); } } else { @@ -994,7 +993,7 @@ function addContextMenuItems() { } // Add Context menu: Summarize - if(prefs_init.summarize && prefs_init.summarize_context_menu && checkAPIIntegration(prefs_init.connection_type && prefs_init.summarize_use_specific_integration, prefs_init.summarize_connection_type)) { + if(prefs_init.summarize && checkAPIIntegration(prefs_init.connection_type && prefs_init.summarize_use_specific_integration, prefs_init.summarize_connection_type)) { addContextMenu(contextMenuID_Summarize); } } diff --git a/options/mzta-options-default.js b/options/mzta-options-default.js index 6fd52d1d..d5ccf9b2 100644 --- a/options/mzta-options-default.js +++ b/options/mzta-options-default.js @@ -134,6 +134,5 @@ export const prefs_default = { spamfilter_context_menu: true, spamfilter_enabled_accounts: [], summarize: false, - summarize_context_menu: true, ...generated_prefs } From cb6c93625b1bbda21b0766c6a25e7073a5285723 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Fri, 16 Jan 2026 13:39:22 +0100 Subject: [PATCH 23/26] correct prompt naming --- _locales/en/messages.json | 8 ++++++++ mzta-background.js | 6 +++--- pages/summarize/mzta-summarize.js | 12 ++++++------ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 2d7329ec..b52a5539 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1016,10 +1016,18 @@ "description": "" }, "prompt_summarize_email_template": { + "message": "Summarize email template", + "description": "" + }, + "prompt_summarize_email_template_full_text": { "message": "From: {%author%}\nTo: {%recipients%}\nCC: {%cc_list%}\nSubject: {%mail_subject%}\nDate: {%mail_datetime%}\nAttachments:\n{%mail_attachments_info%}\nBody:\n{%mail_text_body%}", "description": "" }, "prompt_summarize_email_separator": { + "message": "Email separator", + "description": "" + }, + "prompt_summarize_email_separator_full_text": { "message": "\n\n---------- NEXT EMAIL ----------\n\n", "description": "" }, diff --git a/mzta-background.js b/mzta-background.js index d433581a..e7d2e3ac 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -1029,9 +1029,9 @@ async function summarizeEmails(messages) { // we have three prompts, the actual assignment for the LLM, the email // template prompt, and the email separator prompt const specialPrompts = await getSpecialPrompts(); - const prompt = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize'); - 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 prompt = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize_full_text'); + const prompt_email = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize_email_template_full_text'); + const prompt_email_separator = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize_email_separator_full_text'); const tabs = await browser.tabs.query({ active: true, currentWindow: true }); const chatgpt_lang = await taPromptUtils.getDefaultLang(prompt); diff --git a/pages/summarize/mzta-summarize.js b/pages/summarize/mzta-summarize.js index ea8d8364..8df7d8b7 100644 --- a/pages/summarize/mzta-summarize.js +++ b/pages/summarize/mzta-summarize.js @@ -93,19 +93,19 @@ document.addEventListener("DOMContentLoaded", async () => { }); summarize_textarea_email_template.addEventListener("input", (event) => { - summarize_reset_email_template_btn.disabled = (event.target.value === browser.i18n.getMessage('prompt_summarize_email_template')); + summarize_reset_email_template_btn.disabled = (event.target.value === browser.i18n.getMessage('prompt_summarize_email_template_full_text')); summarize_save_email_template_btn.disabled = (event.target.value === summarize_email_template.text); }); summarize_email_separator_textarea.addEventListener("input", (event) => { - summarize_email_separator_reset_btn.disabled = (event.target.value === browser.i18n.getMessage('prompt_summarize_email_separator')); + summarize_email_separator_reset_btn.disabled = (event.target.value === browser.i18n.getMessage('prompt_summarize_email_separator_full_text')); summarize_email_separator_save_btn.disabled = (event.target.value === summarize_email_separator.text); }); // on clicking buttons, reset summarize_reset_email_template_btn.addEventListener("click", () => { - summarize_textarea_email_template.value = browser.i18n.getMessage("prompt_summarize_email_template"); + summarize_textarea_email_template.value = browser.i18n.getMessage("prompt_summarize_email_template_full_text"); summarize_reset_email_template_btn.disabled = true; let event = new Event("input", { bubbles: true, cancelable: true }); summarize_textarea_email_template.dispatchEvent(event); @@ -119,7 +119,7 @@ document.addEventListener("DOMContentLoaded", async () => { }); summarize_email_separator_reset_btn.addEventListener("click", () => { - summarize_email_separator_textarea.value = browser.i18n.getMessage("prompt_summarize_email_separator"); + summarize_email_separator_textarea.value = browser.i18n.getMessage("prompt_summarize_email_separator_full_text"); summarize_email_separator_reset_btn.disabled = true; let event = new Event("input", { bubbles: true, cancelable: true }); summarize_email_separator_textarea.dispatchEvent(event); @@ -151,10 +151,10 @@ document.addEventListener("DOMContentLoaded", async () => { if(summarize_prompt.text === 'prompt_summarize_full_text'){ summarize_prompt.text = browser.i18n.getMessage(summarize_prompt.text); } - if(summarize_email_template.text === 'prompt_summarize_email_template'){ + if(summarize_email_template.text === 'prompt_summarize_email_template_full_text'){ summarize_email_template.text = browser.i18n.getMessage(summarize_email_template.text); } - if(summarize_email_separator.text === 'prompt_summarize_email_separator'){ + if(summarize_email_separator.text === 'prompt_summarize_email_separator_full_text'){ summarize_email_separator.text = browser.i18n.getMessage(summarize_email_separator.text); } From 7202b5a3e29e11839722a6fd6a4bff8065d0b30a Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Fri, 16 Jan 2026 14:21:35 +0100 Subject: [PATCH 24/26] fix prompt names --- mzta-background.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mzta-background.js b/mzta-background.js index e7d2e3ac..d433581a 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -1029,9 +1029,9 @@ async function summarizeEmails(messages) { // we have three prompts, the actual assignment for the LLM, the email // template prompt, and the email separator prompt const specialPrompts = await getSpecialPrompts(); - const prompt = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize_full_text'); - const prompt_email = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize_email_template_full_text'); - const prompt_email_separator = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize_email_separator_full_text'); + const prompt = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize'); + 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); From b247dad80f1b103de31ff5c0b39dd1099fed3c06 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Fri, 16 Jan 2026 14:22:07 +0100 Subject: [PATCH 25/26] adapt code copied from add_tags --- pages/summarize/mzta-summarize.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/pages/summarize/mzta-summarize.js b/pages/summarize/mzta-summarize.js index 8df7d8b7..71634058 100644 --- a/pages/summarize/mzta-summarize.js +++ b/pages/summarize/mzta-summarize.js @@ -198,9 +198,6 @@ function saveOptions(e) { options[element.id] = element.value; break; case 'textarea': - if(element.id === 'add_tags_auto_uselist_list') { - element.value = normalizeStringList(element.value, 1); - } options[element.id] = normalizeStringList(element.value); break; default: @@ -229,9 +226,6 @@ async function restoreOptions() { case 'password': let default_text_value = ''; if(element.id == 'default_chatgpt_lang') default_text_value = prefs_default.default_chatgpt_lang; - if(element.id === 'add_tags_auto_uselist_list') { - result[element.id] = normalizeStringList(result[element.id], 1); - } element.value = result[element.id] || default_text_value; break; default: @@ -260,21 +254,21 @@ async function restoreOptions() { let getting = await browser.storage.sync.get(prefs_default); let specialPrompts = await getSpecialPrompts(); - let addtags_prompt = specialPrompts.find(prompt => prompt.id === 'prompt_add_tags'); + let addtags_prompt = specialPrompts.find(prompt => prompt.id === 'prompt_summarize'); if (addtags_prompt) { if (addtags_prompt.api_type && addtags_prompt.api_type !== '') { - getting['add_tags_connection_type'] = addtags_prompt.api_type; + getting['summarize_connection_type'] = addtags_prompt.api_type; } else { - getting['add_tags_connection_type'] = getting['connection_type']; + getting['summarize_connection_type'] = getting['connection_type']; } for (const [integration, options] of Object.entries(integration_options_config)) { for (const key of Object.keys(options)) { const propName = `${integration}_${key}`; if (addtags_prompt[propName] !== undefined && addtags_prompt[propName] !== '') { - getting[`add_tags_${propName}`] = addtags_prompt[propName]; + getting[`summarize_${propName}`] = addtags_prompt[propName]; } else { - getting[`add_tags_${propName}`] = getting[propName]; + getting[`summarize_${propName}`] = getting[propName]; } } } From f6fcaac0bb9947ad1117b3ebe07dbfd4b31e28f0 Mon Sep 17 00:00:00 2001 From: Guido Kraemer Date: Fri, 16 Jan 2026 15:09:47 +0100 Subject: [PATCH 26/26] update prompts --- _locales/en/messages.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index b52a5539..3ce55cc4 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1012,7 +1012,7 @@ "description": "" }, "prompt_summarize_full_text": { - "message": "Summarize the following email or emails into a bullet point list. If there are multiple emails, consider the entire thread for the summary.", + "message": "You are an assistant that summarizes email conversations.\n\nGiven an email thread, produce a concise, accurate summary that captures:\n\n- The main topic or purpose of the conversation\n- Key decisions, conclusions, or agreements\n- Important questions, requests, or action items\n- Who is responsible for each action item (if stated)\n\nOmit greetings, signatures, quoted text, and redundant back-and-forth.\nDo not add assumptions or information not present in the emails.\n\nWrite the summary in clear, neutral language suitable for a busy professional.", "description": "" }, "prompt_summarize_email_template": { @@ -1020,7 +1020,7 @@ "description": "" }, "prompt_summarize_email_template_full_text": { - "message": "From: {%author%}\nTo: {%recipients%}\nCC: {%cc_list%}\nSubject: {%mail_subject%}\nDate: {%mail_datetime%}\nAttachments:\n{%mail_attachments_info%}\nBody:\n{%mail_text_body%}", + "message": "From: {%author%}\nTo: {%recipients%}\nCC: {%cc_list%}\nSubject: {%mail_subject%}\nDate: {%mail_datetime%}\nAttachments:\n{%mail_attachments_info%}\n\nBody:\n{%mail_text_body%}", "description": "" }, "prompt_summarize_email_separator": {