From b91b17789f848408e9a73723b73fe46a75ad9631 Mon Sep 17 00:00:00 2001 From: Mic Date: Wed, 4 Feb 2026 22:22:01 +0100 Subject: [PATCH 01/51] Update README with predefined OpenAI Compatible API configurations Added predefined OpenAI Compatible API configurations for various APIs. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 95d1828f..d4b22e06 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,12 @@ Using an API integration, you can activate some automatic features: > - **OpenAI Compatible API** > - You can also use a local OpenAI Compatible API server, like LM Studio or Mistral AI! > - There is also an option to remove the "v1" segment from the API url, if needed, and to manually set the model name if the server doesn't have a models list endpoint. +> - You can also use one of these predefined configurations: +> - DeepSeek API +> - Grok API +> - Mistral API +> - OpenRouter API +> - Perpelxity API From ab3739521e52f8c140b414e3444bfa3c9b327d69 Mon Sep 17 00:00:00 2001 From: Mic Date: Wed, 4 Feb 2026 23:08:08 +0100 Subject: [PATCH 02/51] Fix typo in Perplexity API name --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d4b22e06..617a96ee 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Using an API integration, you can activate some automatic features: > - Grok API > - Mistral API > - OpenRouter API -> - Perpelxity API +> - Perplexity API From 942c88d00e629b8cbf839322302cbde7952b5638 Mon Sep 17 00:00:00 2001 From: Mic Date: Thu, 5 Feb 2026 23:58:00 +0100 Subject: [PATCH 03/51] release notes updated --- CHANGELOG.md | 1 + options/mzta-release-notes.html | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eed3a920..b3f1eeb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@
  • [OpenAI Comp API][Ollama API] Asking for the single host for permission to avoid CORS errors, instead of all_urls, as requested by the Thunderbird Review Team [#524].
  • Fix: Using also the mail folder owner to search for the right identity to use when composing a reply [#627].
  • Context menu items are always ordered alfabetically [#630].
  • +
  • The prompt export now includes an option to incorporate specific API settings, when present [#624].
  • ...
  • Version 3.8.3 - 22/01/2026

    diff --git a/options/mzta-release-notes.html b/options/mzta-release-notes.html index 32b66f1f..097c90ec 100644 --- a/options/mzta-release-notes.html +++ b/options/mzta-release-notes.html @@ -17,6 +17,7 @@
  • [OpenAI Comp API][Ollama API] Asking for the single host for permission to avoid CORS errors, instead of all_urls, as requested by the Thunderbird Review Team [#524].
  • Fix: Using also the mail folder owner to search for the right identity to use when composing a reply [#627].
  • Context menu items are always ordered alfabetically [#630].
  • +
  • The prompt export now includes an option to incorporate specific API settings, when present [#624].
  • ...
  • Version 3.8.3 - 22/01/2026

    From 6cb52e44038550f19317832849194a74e5d29bb8 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 00:00:00 +0100 Subject: [PATCH 04/51] await added to wait before saying that the new prompts are saved. --- pages/customprompts/mzta-custom-prompts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/customprompts/mzta-custom-prompts.js b/pages/customprompts/mzta-custom-prompts.js index 98c0b1bb..58bcbf04 100644 --- a/pages/customprompts/mzta-custom-prompts.js +++ b/pages/customprompts/mzta-custom-prompts.js @@ -1279,7 +1279,7 @@ async function saveAll() { setMessage(browser.i18n.getMessage('customPrompts_saving_custom_prompts')); await setCustomPrompts(newCustomPrompts); setMessage(browser.i18n.getMessage('customPrompts_reloading_menus')); - browser.runtime.sendMessage({command: "reload_menus"}); + await browser.runtime.sendMessage({command: "reload_menus"}); setMessage(browser.i18n.getMessage('customPrompts_saved'),'green'); msgTimeout = setTimeout(() => { clearMessage(); From 5a39f1e2983d0b3d86e4038e65600a4e603dad3d Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 00:06:00 +0100 Subject: [PATCH 05/51] calendar_no_selection option added. see #641 --- _locales/en/messages.json | 8 ++++++++ options/mzta-options-default.js | 1 + pages/get-calendar-event/mzta-get-calendar-event.html | 10 ++++++++++ 3 files changed, 19 insertions(+) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index dc6f5b02..1370c46e 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1808,5 +1808,13 @@ "customPrompts_export_include_api_settings": { "message": "Do you want to include the API settings in the export? Be aware that also the API Key will be saved in the file!", "description": "" + }, + "prefs_OptionText_calendar_no_selection": { + "message": "Do not ask to select text", + "description": "" + }, + "prefs_OptionText_calendar_no_selection_Info": { + "message": "If checked there is no need to select some text. The full message body will be used to get the calendar event.", + "description": "" } } diff --git a/options/mzta-options-default.js b/options/mzta-options-default.js index eb8cda9c..64b5d839 100644 --- a/options/mzta-options-default.js +++ b/options/mzta-options-default.js @@ -128,6 +128,7 @@ export const prefs_default = { get_task: true, calendar_enforce_timezone: false, calendar_timezone: '', + calendar_no_selection: false, // If true do not ask for selection, but use the full prompt spamfilter: false, spamfilter_threshold: 70, spamfilter_enabled_accounts: [], diff --git a/pages/get-calendar-event/mzta-get-calendar-event.html b/pages/get-calendar-event/mzta-get-calendar-event.html index ee036aba..095f26aa 100644 --- a/pages/get-calendar-event/mzta-get-calendar-event.html +++ b/pages/get-calendar-event/mzta-get-calendar-event.html @@ -26,6 +26,16 @@ + + __MSG_prefs_OptionText_calendar_no_selection__ + + + + + __MSG_prefs_OptionText_calendar_enforce_timezone__ From db8cada6e63f280be88dabe506fb9cba0852eec9 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 00:08:00 +0100 Subject: [PATCH 06/51] mail_body_or_selected_text and mail_body_or_selected_html placeholders added. see #641 --- _locales/en/messages.json | 10 +++++++++- js/mzta-placeholders.js | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 1370c46e..b62f87c5 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -659,6 +659,14 @@ "message": "Account email address", "description": "" }, + "placeholder_mail_text_body_or_selected": { + "message": "Mail body or selected text", + "description": "" + }, + "placeholder_mail_html_body_or_selected": { + "message": "Mail body or selected HTML", + "description": "" + }, "prefs_OptionText_placeholders_use_default_value": { "message": "Placeholders: use default value", "description": "" @@ -1000,7 +1008,7 @@ "description": "" }, "prompt_get_calendar_event_full_text": { - "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%}\"", + "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:\"{%mail_text_body_or_selected%}\"", "description": "" }, "Summarize_prompt_prefs_title": { diff --git a/js/mzta-placeholders.js b/js/mzta-placeholders.js index bfb841c7..d8b63008 100644 --- a/js/mzta-placeholders.js +++ b/js/mzta-placeholders.js @@ -267,6 +267,24 @@ const defaultPlaceholders = [ is_default: "1", is_dynamic: "0", enabled: 1, + }, + { + id: 'mail_text_body_or_selected', + name: "__MSG_placeholder_mail_text_body_or_selected__", + default_value: "", + type: 0, + is_default: "1", + is_dynamic: "0", + enabled: 1, + }, + { + id: 'mail_html_body_or_selected', + name: "__MSG_placeholder_mail_html_body_or_selected__", + default_value: "", + type: 0, + is_default: "1", + is_dynamic: "0", + enabled: 1, } ]; @@ -517,6 +535,12 @@ export const placeholdersUtils = { case 'selected_html': finalSubs['selected_html'] = placeholdersUtils.failSafePlaceholders(selection_html); break; + case 'mail_text_body_or_selected': + finalSubs['mail_text_body_or_selected'] = placeholdersUtils.failSafePlaceholders(selection_text || body_text); + break; + case 'mail_html_body_or_selected': + finalSubs['mail_html_body_or_selected'] = placeholdersUtils.failSafePlaceholders(selection_html || msg_text?.html); + break; case 'author': finalSubs['author'] = placeholdersUtils.failSafePlaceholders(curr_message.author); break; From 632bf6c38a87a70ce9d8aa5bb3cc3e4e0a95a728 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 00:16:00 +0100 Subject: [PATCH 07/51] using calendar_no_selection option and updated the default prompt. see #518 --- pages/get-calendar-event/mzta-get-calendar-event.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pages/get-calendar-event/mzta-get-calendar-event.js b/pages/get-calendar-event/mzta-get-calendar-event.js index 2b956440..584fb244 100644 --- a/pages/get-calendar-event/mzta-get-calendar-event.js +++ b/pages/get-calendar-event/mzta-get-calendar-event.js @@ -78,6 +78,7 @@ document.addEventListener('DOMContentLoaded', async () => { let get_calendar_event_save_btn = document.getElementById('btn_save_prompt'); let get_calendar_event_reset_btn = document.getElementById('btn_reset_prompt'); let get_calendar_event_use_specific_integration = document.getElementById('get_calendar_event_use_specific_integration'); + let get_calendar_event_no_selection = document.getElementById('calendar_no_selection'); get_calendar_event_textarea.addEventListener('input', (event) => { get_calendar_event_reset_btn.disabled = (event.target.value === browser.i18n.getMessage('prompt_get_calendar_event_full_text')); @@ -95,6 +96,12 @@ document.addEventListener('DOMContentLoaded', async () => { } }); + get_calendar_event_no_selection.addEventListener('change', async (event) => { + specialPrompts.find(prompt => prompt.id === 'prompt_get_calendar_event').need_selected = event.target.checked ? '0' : '1'; + await setSpecialPrompts(specialPrompts); + browser.runtime.sendMessage({command: "reload_menus"}); + }); + get_calendar_event_reset_btn.addEventListener('click', () => { get_calendar_event_textarea.value = browser.i18n.getMessage('prompt_get_calendar_event_full_text'); get_calendar_event_reset_btn.disabled = true; @@ -102,9 +109,9 @@ document.addEventListener('DOMContentLoaded', async () => { get_calendar_event_textarea.dispatchEvent(event); }); - get_calendar_event_save_btn.addEventListener('click', () => { + get_calendar_event_save_btn.addEventListener('click', async () => { specialPrompts.find(prompt => prompt.id === 'prompt_get_calendar_event').text = get_calendar_event_textarea.value; - setSpecialPrompts(specialPrompts); + await setSpecialPrompts(specialPrompts); get_calendar_event_save_btn.disabled = true; document.getElementById('get_calendar_event_prompt_unsaved').classList.add('hidden'); browser.runtime.sendMessage({command: "reload_menus"}); From 20d23cc9518f90971fd2531aae99d5d7d5acea83 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 00:22:00 +0100 Subject: [PATCH 08/51] release notes updated --- CHANGELOG.md | 3 +++ options/mzta-release-notes.html | 3 +++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3f1eeb5..ba10cb4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@
  • Fix: Using also the mail folder owner to search for the right identity to use when composing a reply [#627].
  • Context menu items are always ordered alfabetically [#630].
  • The prompt export now includes an option to incorporate specific API settings, when present [#624].
  • +
  • Added the {%mail_text_body_or_selected%} placeholder to retrieve the selected text or the full text body of the email if no selection is present [#641].
  • +
  • Added the {%mail_html_body_or_selected%} placeholder to retrieve the selected HTML or the full HTML body of the email if no selection is present [#641].
  • +
  • [All APIs] Added an option to get a calendar event without selecting some text, but using the full text body of the email [#518].
  • ...
  • Version 3.8.3 - 22/01/2026

    diff --git a/options/mzta-release-notes.html b/options/mzta-release-notes.html index 097c90ec..5f24e389 100644 --- a/options/mzta-release-notes.html +++ b/options/mzta-release-notes.html @@ -18,6 +18,9 @@
  • Fix: Using also the mail folder owner to search for the right identity to use when composing a reply [#627].
  • Context menu items are always ordered alfabetically [#630].
  • The prompt export now includes an option to incorporate specific API settings, when present [#624].
  • +
  • Added the {%mail_text_body_or_selected%} placeholder to retrieve the selected text or the full text body of the email if no selection is present [#641].
  • +
  • Added the {%mail_html_body_or_selected%} placeholder to retrieve the selected HTML or the full HTML body of the email if no selection is present [#641].
  • +
  • [All APIs] Added an option to get a calendar event without selecting some text, but using the full text body of the email [#518].
  • ...
  • Version 3.8.3 - 22/01/2026

    From 7c971218a37ab3d925de5bfc57454387db3397e7 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 00:29:00 +0100 Subject: [PATCH 09/51] gemini info updated --- GEMINI.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/GEMINI.md b/GEMINI.md index 1eb93734..69cf47a3 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -18,6 +18,9 @@ ThunderAI is a Mozilla Thunderbird add-on that integrates Large Language Models - **i18n:** - The project supports multiple languages via `_locales/`. When adding UI strings, always use `messenger.i18n.getMessage()`. - When you need to modify language file, modify only the english version. +- **Logging:** + - When possibile use the taLog object to log errors, otherwise use the console.error() method. + - If it's useful to log add debug logs using taLog.log() method. ## Files & Directories Structure - `manifest.json`: Extension entry point and permission definitions. From 184456002774cc254a0fdbf051e780c3044a88e5 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 00:42:00 +0100 Subject: [PATCH 10/51] adding a calendar event from clipboard. see #362 --- _locales/en/messages.json | 28 ++++++++++ js/mzta-menus.js | 21 +++++++- js/mzta-prompts.js | 15 ++++++ js/mzta-utils.js | 4 ++ manifest.json | 1 + mzta-background.js | 31 ++++++++++- options/mzta-options-default.js | 1 + .../mzta-get-calendar-event.html | 10 ++++ .../mzta-get-calendar-event.js | 30 +++++++++++ popup/mzta-popup.js | 52 +++++++++++++++---- 10 files changed, 182 insertions(+), 11 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index b62f87c5..87095fa4 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1011,6 +1011,34 @@ "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:\"{%mail_text_body_or_selected%}\"", "description": "" }, + "prompt_get_calendar_event_from_clipboard": { + "message": "Add calendar event from clipboard", + "description": "" + }, + "clipboard_read_error": { + "message": "Could not read clipboard. Please check permissions.", + "description": "" + }, + "clipboard_empty_error": { + "message": "Clipboard is empty. Please copy some text first.", + "description": "" + }, + "clipboard_permission_denied": { + "message": "Clipboard permission was denied. Please enable the feature again in settings to grant permission.", + "description": "" + }, + "clipboard_permission_error": { + "message": "Error requesting clipboard permission. Please try again.", + "description": "" + }, + "prefs_OptionText_get_calendar_event_from_clipboard": { + "message": "Get calendar event from clipboard", + "description": "" + }, + "prefs_OptionText_get_calendar_event_from_clipboard_Info": { + "message": "Show an additional menu item to create calendar events from clipboard text content.", + "description": "" + }, "Summarize_prompt_prefs_title": { "message": "Add Summarization Options", "description": "" diff --git a/js/mzta-menus.js b/js/mzta-menus.js index b164bae1..a2d50436 100644 --- a/js/mzta-menus.js +++ b/js/mzta-menus.js @@ -110,6 +110,24 @@ export class mzta_Menus { taWorkingStatus.startWorking(); const tabs = await browser.tabs.query({ active: true, currentWindow: true }); const msg_text = await getMailBody(tabs, placeholdersUtils.hasPlaceholder(curr_prompt.text,'mail_typed_text')); + + if (curr_prompt.id === 'prompt_get_calendar_event_from_clipboard') { + try { + const clipboardText = await navigator.clipboard.readText(); + if (!clipboardText) { + browser.tabs.sendMessage(tabs[0].id, { command: "sendAlert", curr_tab_type: tabs[0].type, message: browser.i18n.getMessage('clipboard_empty_error') }); + taWorkingStatus.stopWorking(); + return {ok:'0'}; + } + msg_text.selection = clipboardText; + } catch (e) { + console.error("Clipboard read error:", e); + browser.tabs.sendMessage(tabs[0].id, { command: "sendAlert", curr_tab_type: tabs[0].type, message: browser.i18n.getMessage('clipboard_read_error') }); + taWorkingStatus.stopWorking(); + return {ok:'0'}; + } + } + // console.log(">>>>>>>>>>>>> msg_text: " + JSON.stringify(msg_text)); //check if a selection is needed if(String(curr_prompt.need_selected) == "1" && (msg_text.selection==='')){ @@ -256,7 +274,8 @@ export class mzta_Menus { return {ok:'1'}; break; // Add tags to the email - END } - case 'prompt_get_calendar_event': { // Get a calendar event info + case 'prompt_get_calendar_event': // Get a calendar event info + case 'prompt_get_calendar_event_from_clipboard': { // here from clipboard let calendar_event_data = ''; let prefs_at = await browser.storage.sync.get({ connection_type: prefs_default.connection_type, diff --git a/js/mzta-prompts.js b/js/mzta-prompts.js index a3fac86e..4a73f20a 100644 --- a/js/mzta-prompts.js +++ b/js/mzta-prompts.js @@ -294,6 +294,21 @@ const specialPrompts = [ is_default: "1", is_special: "1", }, + { + id: 'prompt_get_calendar_event_from_clipboard', + name: "__MSG_prompt_get_calendar_event_from_clipboard__", + text: "prompt_get_calendar_event_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: '', + is_default: "1", + is_special: "1", + }, { id: 'prompt_get_task', name: "__MSG_prompt_get_task__", diff --git a/js/mzta-utils.js b/js/mzta-utils.js index 90feb52b..a8aabdb2 100644 --- a/js/mzta-utils.js +++ b/js/mzta-utils.js @@ -577,6 +577,7 @@ export function getActiveSpecialPromptsIDs(args = {}) { addtags = false, addtags_api = false, get_calendar_event = false, + get_calendar_event_from_clipboard = false, get_task = false, is_chatgpt_web = false } = args; @@ -596,6 +597,9 @@ export function getActiveSpecialPromptsIDs(args = {}) { if (get_calendar_event) { output.push('prompt_get_calendar_event'); } + if (get_calendar_event_from_clipboard) { + output.push('prompt_get_calendar_event_from_clipboard'); + } if (get_task) { output.push('prompt_get_task'); } diff --git a/manifest.json b/manifest.json index bb91cd1f..743688ab 100644 --- a/manifest.json +++ b/manifest.json @@ -34,6 +34,7 @@ "messagesTagsList" ], "optional_permissions": [ + "clipboardRead", "messagesTags", "messagesUpdate", "messagesMove", diff --git a/mzta-background.js b/mzta-background.js index cbdfbc7c..5fee2dd6 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -99,6 +99,7 @@ let special_prompts_ids = getActiveSpecialPromptsIDs({ addtags: prefs_init.add_tags, addtags_api: hasSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), get_calendar_event: doGetSparkFeature(prefs_init.get_calendar_event), + get_calendar_event_from_clipboard: doGetSparkFeature(prefs_init.get_calendar_event_from_clipboard), get_task: doGetSparkFeature(prefs_init.get_task), is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") }); @@ -183,13 +184,15 @@ function preparePopupMenu(tab) { } async function _reload_menus() { - let prefs_reload = await browser.storage.sync.get({add_tags: prefs_default.add_tags, get_calendar_event: prefs_default.get_calendar_event, get_task: prefs_default.get_task, connection_type: prefs_default.connection_type}); + let prefs_reload = await browser.storage.sync.get({add_tags: prefs_default.add_tags, get_calendar_event: prefs_default.get_calendar_event, get_calendar_event_from_clipboard: prefs_default.get_calendar_event_from_clipboard, get_task: prefs_default.get_task, connection_type: prefs_default.connection_type}); let getCalendarEvent = doGetSparkFeature(prefs_reload.get_calendar_event); + let getCalendarEventFromClipboard = doGetSparkFeature(prefs_reload.get_calendar_event_from_clipboard); let getTask = doGetSparkFeature(prefs_reload.get_task); const special_prompts_ids = getActiveSpecialPromptsIDs({ addtags: prefs_reload.add_tags, addtags_api: hasSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), get_calendar_event: getCalendarEvent, + get_calendar_event_from_clipboard: getCalendarEventFromClipboard, get_task: getTask, is_chatgpt_web: (prefs_reload.connection_type === "chatgpt_web") }); @@ -803,6 +806,7 @@ async function reload_pref_init(){ do_debug: prefs_default.do_debug, add_tags: prefs_default.add_tags, get_calendar_event: prefs_default.get_calendar_event, + get_calendar_event_from_clipboard: prefs_default.get_calendar_event_from_clipboard, get_task: prefs_default.get_task, connection_type: prefs_default.connection_type, add_tags_auto: prefs_default.add_tags_auto, @@ -828,11 +832,13 @@ function setupStorageChangeListener() { if (changes.add_tags) { const newTags = changes.add_tags.newValue; let getCalendarEvent = doGetSparkFeature(prefs_init.get_calendar_event); + let getCalendarEventFromClipboard = doGetSparkFeature(prefs_init.get_calendar_event_from_clipboard); let getTask = doGetSparkFeature(prefs_init.get_task); const special_prompts_ids = getActiveSpecialPromptsIDs({ addtags: newTags, addtags_api: hasSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), get_calendar_event: getCalendarEvent, + get_calendar_event_from_clipboard: getCalendarEventFromClipboard, get_task: getTask, is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") }); @@ -843,11 +849,30 @@ function setupStorageChangeListener() { if (changes.get_calendar_event) { const newCalendarEvent = changes.get_calendar_event.newValue; let getCalendarEvent = doGetSparkFeature(newCalendarEvent); + let getCalendarEventFromClipboard = doGetSparkFeature(prefs_init.get_calendar_event_from_clipboard); let getTask = doGetSparkFeature(prefs_init.get_task); const special_prompts_ids = getActiveSpecialPromptsIDs({ addtags: prefs_init.add_tags, addtags_api: hasSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), get_calendar_event: getCalendarEvent, + get_calendar_event_from_clipboard: getCalendarEventFromClipboard, + get_task: getTask, + is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") + }); + menus.reload(special_prompts_ids); + } + + // Process 'get_calendar_event_from_clipboard' changes + if (changes.get_calendar_event_from_clipboard) { + const newCalendarEventFromClipboard = changes.get_calendar_event_from_clipboard.newValue; + let getCalendarEvent = doGetSparkFeature(prefs_init.get_calendar_event); + let getCalendarEventFromClipboard = doGetSparkFeature(newCalendarEventFromClipboard); + let getTask = doGetSparkFeature(prefs_init.get_task); + const special_prompts_ids = getActiveSpecialPromptsIDs({ + addtags: prefs_init.add_tags, + addtags_api: hasSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), + get_calendar_event: getCalendarEvent, + get_calendar_event_from_clipboard: getCalendarEventFromClipboard, get_task: getTask, is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") }); @@ -858,11 +883,13 @@ function setupStorageChangeListener() { if (changes.get_task) { const newTask = changes.get_task.newValue; let getCalendarEvent = doGetSparkFeature(prefs_init.get_calendar_event); + let getCalendarEventFromClipboard = doGetSparkFeature(prefs_init.get_calendar_event_from_clipboard); let getTask = doGetSparkFeature(newTask); const special_prompts_ids = getActiveSpecialPromptsIDs({ addtags: prefs_init.add_tags, addtags_api: hasSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), get_calendar_event: getCalendarEvent, + get_calendar_event_from_clipboard: getCalendarEventFromClipboard, get_task: getTask, is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") }); @@ -873,11 +900,13 @@ function setupStorageChangeListener() { if (changes.connection_type) { const newConnectionType = changes.connection_type.newValue; let getCalendarEvent = doGetSparkFeature(prefs_init.get_calendar_event); + let getCalendarEventFromClipboard = doGetSparkFeature(prefs_init.get_calendar_event_from_clipboard); let getTask = doGetSparkFeature(prefs_init.get_task); const special_prompts_ids = getActiveSpecialPromptsIDs({ addtags: prefs_init.add_tags, addtags_api: hasSpecificIntegration(prefs_init.add_tags_use_specific_integration, prefs_init.add_tags_connection_type), get_calendar_event: getCalendarEvent, + get_calendar_event_from_clipboard: getCalendarEventFromClipboard, get_task: getTask, is_chatgpt_web: (newConnectionType === "chatgpt_web") }); diff --git a/options/mzta-options-default.js b/options/mzta-options-default.js index 64b5d839..408ad38d 100644 --- a/options/mzta-options-default.js +++ b/options/mzta-options-default.js @@ -125,6 +125,7 @@ export const prefs_default = { add_tags_auto_uselist_list: '', add_tags_enabled_accounts: [], get_calendar_event: true, + get_calendar_event_from_clipboard: false, // Enable calendar event from clipboard menu item get_task: true, calendar_enforce_timezone: false, calendar_timezone: '', diff --git a/pages/get-calendar-event/mzta-get-calendar-event.html b/pages/get-calendar-event/mzta-get-calendar-event.html index 095f26aa..53366e35 100644 --- a/pages/get-calendar-event/mzta-get-calendar-event.html +++ b/pages/get-calendar-event/mzta-get-calendar-event.html @@ -36,6 +36,16 @@ + + __MSG_prefs_OptionText_get_calendar_event_from_clipboard__ + + + + + __MSG_prefs_OptionText_calendar_enforce_timezone__ diff --git a/pages/get-calendar-event/mzta-get-calendar-event.js b/pages/get-calendar-event/mzta-get-calendar-event.js index 584fb244..c2bb8889 100644 --- a/pages/get-calendar-event/mzta-get-calendar-event.js +++ b/pages/get-calendar-event/mzta-get-calendar-event.js @@ -79,6 +79,7 @@ document.addEventListener('DOMContentLoaded', async () => { let get_calendar_event_reset_btn = document.getElementById('btn_reset_prompt'); let get_calendar_event_use_specific_integration = document.getElementById('get_calendar_event_use_specific_integration'); let get_calendar_event_no_selection = document.getElementById('calendar_no_selection'); + let get_calendar_event_from_clipboard = document.getElementById('get_calendar_event_from_clipboard'); get_calendar_event_textarea.addEventListener('input', (event) => { get_calendar_event_reset_btn.disabled = (event.target.value === browser.i18n.getMessage('prompt_get_calendar_event_full_text')); @@ -102,6 +103,35 @@ document.addEventListener('DOMContentLoaded', async () => { browser.runtime.sendMessage({command: "reload_menus"}); }); + get_calendar_event_from_clipboard.addEventListener('change', async (event) => { + if (event.target.checked) { + // Request clipboardRead permission when enabling the feature + try { + const granted = await browser.permissions.request({ + permissions: ["clipboardRead"] + }); + + if (granted) { + // Permission granted, enable the feature + await browser.storage.sync.set({ get_calendar_event_from_clipboard: true }); + browser.runtime.sendMessage({command: "reload_menus"}); + } else { + // Permission denied, uncheck the checkbox + event.target.checked = false; + alert(browser.i18n.getMessage("clipboard_permission_denied")); + } + } catch (err) { + taLog.error("Error requesting clipboard permission:", err); + event.target.checked = false; + alert(browser.i18n.getMessage("clipboard_permission_error")); + } + } else { + // Disabling the feature + await browser.storage.sync.set({ get_calendar_event_from_clipboard: false }); + browser.runtime.sendMessage({command: "reload_menus"}); + } + }); + get_calendar_event_reset_btn.addEventListener('click', () => { get_calendar_event_textarea.value = browser.i18n.getMessage('prompt_get_calendar_event_full_text'); get_calendar_event_reset_btn.disabled = true; diff --git a/popup/mzta-popup.js b/popup/mzta-popup.js index d5d69664..c50bc028 100644 --- a/popup/mzta-popup.js +++ b/popup/mzta-popup.js @@ -30,6 +30,7 @@ let add_tags = false; let add_tags_use_specific_integration = false; let add_tags_connection_type = ''; let get_calendar_event = false; +let get_calendar_event_from_clipboard = false; let get_task = false; let _ok_sparks = false; let tabType; @@ -43,6 +44,7 @@ document.addEventListener('DOMContentLoaded', async () => { add_tags_use_specific_integration: prefs_default.add_tags_use_specific_integration, add_tags_connection_type: prefs_default.add_tags_connection_type, get_calendar_event: prefs_default.get_calendar_event, + get_calendar_event_from_clipboard: prefs_default.get_calendar_event_from_clipboard, get_task: prefs_default.get_task, connection_type: prefs_default.connection_type }); @@ -66,6 +68,7 @@ document.addEventListener('DOMContentLoaded', async () => { add_tags_use_specific_integration = prefs.add_tags_use_specific_integration; add_tags_connection_type = prefs.add_tags_connection_type; get_calendar_event = prefs.get_calendar_event; + get_calendar_event_from_clipboard = prefs.get_calendar_event_from_clipboard; get_task = prefs.get_task; _ok_sparks = await checkSparksPresence() == 1; // console.log(">>>>>>>>>>>>>>>>> add_tags: " + add_tags); @@ -164,6 +167,7 @@ async function searchPrompt(allPrompts, tabId, tabType){ let do_add_tags = checkDoAddTags(); let do_get_calendar_event = checkDoCalendarEvent(); + let do_get_calendar_event_from_clipboard = checkDoCalendarEventFromClipboard(); let do_get_task = checkDoTask(); // console.log(">>>>>>>>>>> do_add_tags: " + do_add_tags); @@ -171,7 +175,7 @@ async function searchPrompt(allPrompts, tabId, tabType){ // console.log(">>>>>>>>>>> do_get_task: " + do_get_task); // console.log(">>>>>>>>>>> filteredData: " + JSON.stringify(filteredData)); - num_special_menu_items = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0) + (do_get_task ? 1 : 0); + num_special_menu_items = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0) + (do_get_calendar_event_from_clipboard ? 1 : 0) + (do_get_task ? 1 : 0); // console.log(">>>>>>>>>>>> num_special_menu_items: " + num_special_menu_items); if(num_special_menu_items > 0){ max_num_el -= num_special_menu_items; @@ -192,10 +196,18 @@ async function searchPrompt(allPrompts, tabId, tabType){ filteredData[gce_curr_pos].numberPrepended = 'true'; filteredData[gce_curr_pos].label = gce_curr_pos + '. ' + filteredData[gce_curr_pos].label; } + } + if(do_get_calendar_event_from_clipboard){ + filteredData = ensurePromptGetCalendarEventFromClipboardFirst(filteredData, do_add_tags, do_get_calendar_event); + let gcefc_curr_pos = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0); + if (!filteredData[gcefc_curr_pos].numberPrepended) { + filteredData[gcefc_curr_pos].numberPrepended = 'true'; + filteredData[gcefc_curr_pos].label = gcefc_curr_pos + '. ' + filteredData[gcefc_curr_pos].label; + } } if(do_get_task){ - filteredData = ensurePromptGetTaskFirst(filteredData, do_add_tags, do_get_calendar_event); - let gtask_curr_pos = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0); + filteredData = ensurePromptGetTaskFirst(filteredData, do_add_tags, do_get_calendar_event, do_get_calendar_event_from_clipboard); + let gtask_curr_pos = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0) + (do_get_calendar_event_from_clipboard ? 1 : 0); if (!filteredData[gtask_curr_pos].numberPrepended) { filteredData[gtask_curr_pos].numberPrepended = 'true'; filteredData[gtask_curr_pos].label = gtask_curr_pos + '. ' + filteredData[gtask_curr_pos].label; @@ -222,7 +234,7 @@ async function searchPrompt(allPrompts, tabId, tabType){ itemDiv.classList.add('mzta_autocomplete-item'); itemDiv.textContent = item.label; itemDiv.setAttribute('data-id', item.id); - if((item.id === 'prompt_add_tags')||(item.id === 'prompt_get_calendar_event')||(item.id === 'prompt_get_task')){ + if((item.id === 'prompt_add_tags')||(item.id === 'prompt_get_calendar_event')||(item.id === 'prompt_get_calendar_event_from_clipboard')||(item.id === 'prompt_get_task')){ itemDiv.className += ' special_prompt'; } @@ -388,6 +400,10 @@ function checkDoCalendarEvent(){ return get_calendar_event && (connection_type !== "chatgpt_web" && tabType !== 'messageCompose') && _ok_sparks; } +function checkDoCalendarEventFromClipboard(){ + return get_calendar_event_from_clipboard && (connection_type !== "chatgpt_web" && tabType !== 'messageCompose') && _ok_sparks; +} + function checkDoTask(){ return get_task && (connection_type !== "chatgpt_web" && tabType !== 'messageCompose') && _ok_sparks; } @@ -424,18 +440,36 @@ function ensurePromptGetCalendarEventFirst(arr, do_add_tags) { return arr; } -function ensurePromptGetTaskFirst(arr, do_add_tags, do_get_calendar_event) { +function ensurePromptGetCalendarEventFromClipboardFirst(arr, do_add_tags, do_get_calendar_event) { + // Find the index of the object with id "prompt_get_calendar_event_from_clipboard" + const index = arr.findIndex(item => item.id === "prompt_get_calendar_event_from_clipboard"); + + const targetPosition = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0); + + // If found and needs repositioning + if (index !== -1 && index !== targetPosition) { + // Remove it from its current position + const [promptAddTags] = arr.splice(index, 1); + + // Add it to the specified position + arr.splice(targetPosition, 0, promptAddTags); + } + + return arr; +} + +function ensurePromptGetTaskFirst(arr, do_add_tags, do_get_calendar_event, do_get_calendar_event_from_clipboard) { // Find the index of the object with id "prompt_get_task" const index = arr.findIndex(item => item.id === "prompt_get_task"); + // Determine the target position to insert "prompt_get_task" after calendar + const targetPosition = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0) + (do_get_calendar_event_from_clipboard ? 1 : 0); + // If found and needs repositioning - if (index !== -1 && ((do_get_calendar_event && do_add_tags) ? index !== 2 : (do_get_calendar_event ? index !== 1 : index !== 0))) { + if (index !== -1 && index !== targetPosition) { // Remove it from its current position const [promptGetTask] = arr.splice(index, 1); - // Determine the target position to insert "prompt_get_task" after calendar - const targetPosition = do_add_tags && do_get_calendar_event ? 2 : (do_get_calendar_event ? 1 : 0); - // Add it to the specified position arr.splice(targetPosition, 0, promptGetTask); } From f13ba8444492378fda01e0845bbc691246bcea9e Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 00:51:00 +0100 Subject: [PATCH 11/51] release notes updated --- CHANGELOG.md | 1 + options/mzta-release-notes.html | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba10cb4d..10589696 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@
  • Added the {%mail_text_body_or_selected%} placeholder to retrieve the selected text or the full text body of the email if no selection is present [#641].
  • Added the {%mail_html_body_or_selected%} placeholder to retrieve the selected HTML or the full HTML body of the email if no selection is present [#641].
  • [All APIs] Added an option to get a calendar event without selecting some text, but using the full text body of the email [#518].
  • +
  • [All APIs] Added a new menu item to create a calendar event from the text saved in the clipboard [#362].
  • ...
  • Version 3.8.3 - 22/01/2026

    diff --git a/options/mzta-release-notes.html b/options/mzta-release-notes.html index 5f24e389..752d1aad 100644 --- a/options/mzta-release-notes.html +++ b/options/mzta-release-notes.html @@ -21,6 +21,7 @@
  • Added the {%mail_text_body_or_selected%} placeholder to retrieve the selected text or the full text body of the email if no selection is present [#641].
  • Added the {%mail_html_body_or_selected%} placeholder to retrieve the selected HTML or the full HTML body of the email if no selection is present [#641].
  • [All APIs] Added an option to get a calendar event without selecting some text, but using the full text body of the email [#518].
  • +
  • [All APIs] Added a new menu item to create a calendar event from the text saved in the clipboard [#362].
  • ...
  • Version 3.8.3 - 22/01/2026

    From 6bbe521e12fff566367813809b8446b48a1d2f12 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 00:51:00 +0100 Subject: [PATCH 12/51] saving also prompt_get_calendar_event_from_clipboard. see #362 --- pages/get-calendar-event/mzta-get-calendar-event.js | 1 + 1 file changed, 1 insertion(+) diff --git a/pages/get-calendar-event/mzta-get-calendar-event.js b/pages/get-calendar-event/mzta-get-calendar-event.js index c2bb8889..7dc9a0e9 100644 --- a/pages/get-calendar-event/mzta-get-calendar-event.js +++ b/pages/get-calendar-event/mzta-get-calendar-event.js @@ -141,6 +141,7 @@ document.addEventListener('DOMContentLoaded', async () => { get_calendar_event_save_btn.addEventListener('click', async () => { specialPrompts.find(prompt => prompt.id === 'prompt_get_calendar_event').text = get_calendar_event_textarea.value; + specialPrompts.find(prompt => prompt.id === 'prompt_get_calendar_event_from_clipboard').text = get_calendar_event_textarea.value; await setSpecialPrompts(specialPrompts); get_calendar_event_save_btn.disabled = true; document.getElementById('get_calendar_event_prompt_unsaved').classList.add('hidden'); From 829f87a510b4d1f53d35b45abbe75359b4a27964 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 00:51:00 +0100 Subject: [PATCH 13/51] saving the clipboard text also in the curr_prompt.text. see #362 --- js/mzta-menus.js | 1 + 1 file changed, 1 insertion(+) diff --git a/js/mzta-menus.js b/js/mzta-menus.js index a2d50436..4d04dcaf 100644 --- a/js/mzta-menus.js +++ b/js/mzta-menus.js @@ -120,6 +120,7 @@ export class mzta_Menus { return {ok:'0'}; } msg_text.selection = clipboardText; + msg_text.text = clipboardText; } catch (e) { console.error("Clipboard read error:", e); browser.tabs.sendMessage(tabs[0].id, { command: "sendAlert", curr_tab_type: tabs[0].type, message: browser.i18n.getMessage('clipboard_read_error') }); From d7321fb0a9bd81b01279b6f63b352344c0cc0d17 Mon Sep 17 00:00:00 2001 From: Andreas Pettersson Date: Thu, 5 Feb 2026 20:26:29 +0100 Subject: [PATCH 14/51] Translated using Weblate (Swedish) Currently translated at 100.0% (449 of 449 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/sv/ --- _locales/sv/messages.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/_locales/sv/messages.json b/_locales/sv/messages.json index 2472ef2d..517e4205 100644 --- a/_locales/sv/messages.json +++ b/_locales/sv/messages.json @@ -1352,5 +1352,8 @@ }, "CORS_localhost_warn": { "message": "Om du använder localhost eller 127.0.0.1 eftersom AI-servern finns på din dator krävs behörigheten ." + }, + "prefs_OptionText_get_calendar_event_use_specific_integration_Info": { + "message": "Om markerat kommer modellen och API:et som anges nedan att användas för att skapa kalenderhändelser, oavsett vilket som valts på ThunderAI-alternativsidan." } } From c34fe07e66c474aa672ccc07a1a0e0da25f815bc Mon Sep 17 00:00:00 2001 From: bittin1ddc447d824349b2 Date: Fri, 6 Feb 2026 10:39:28 +0100 Subject: [PATCH 15/51] Translated using Weblate (Swedish) Currently translated at 100.0% (450 of 450 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/sv/ --- _locales/sv/messages.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/_locales/sv/messages.json b/_locales/sv/messages.json index 517e4205..1d315036 100644 --- a/_locales/sv/messages.json +++ b/_locales/sv/messages.json @@ -1355,5 +1355,8 @@ }, "prefs_OptionText_get_calendar_event_use_specific_integration_Info": { "message": "Om markerat kommer modellen och API:et som anges nedan att användas för att skapa kalenderhändelser, oavsett vilket som valts på ThunderAI-alternativsidan." + }, + "customPrompts_export_include_api_settings": { + "message": "Vill du inkludera API-inställningar i exporten? Tänk på att även API-nyckeln kommer att sparas i filen!" } } From cf7c58852e8096a98eb2c6a5b50ece72f1cf80f1 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 18:58:46 +0100 Subject: [PATCH 16/51] Translated using Weblate (Swedish) Currently translated at 97.6% (450 of 461 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/sv/ --- _locales/sv/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_locales/sv/messages.json b/_locales/sv/messages.json index 1d315036..d3468983 100644 --- a/_locales/sv/messages.json +++ b/_locales/sv/messages.json @@ -1279,7 +1279,7 @@ "message": "Citerad text i meddelandets brödtext" }, "prompt_get_calendar_event_full_text": { - "message": "Extrahera all relevant information som krävs för att generera en kalenderhändelse från följande text. Den extraherade informationen bör innehålla:\n- Händelsetitel\n- Startdatum och tid (inklusive tidszon, om angiven)\n- Slutdatum och tid (inklusive tidszon, om angiven)\n- Heldag (om angiven)\n- Deltagare\nSe till att informationen är tydligt och konsekvent formaterad så att den kan användas direkt för att skapa en kalenderhändelse.\nOm det finns relativa tidsreferenser, tänk på att datum och tid för e-postmeddelandet är \"{%mail_datetime%}\". Beräkna startdatum och tid baserat på denna referens. Om det beräknade startdatumet och tiden är tidigare än \"{%current_datetime%}\", beräkna om startdatumet och tiden med \"{%current_datetime%}\" som bas.\nOm varaktigheten inte anges, sätt den till en timme.\nDessa är deltagarna: {%author%}, {%recipients%}, {%cc_list%}. Om det finns, exkludera min adress: {%account_email_address%}.\nOm du inte kan få en eller flera av de obligatoriska uppgifterna, vänligen svara med en tom sträng.\nGenerera ett svar endast i JSON-format. Inkludera inte ytterligare text eller förklaringar; ange endast JSON. Här är formatet som ska användas:\n{\n\"startDate\": \"ÅÅÅÅMMDDTHHMMSS\",\n\"endDate\": \"ÅÅÅÅMMDDTHHMMSS\",\n\"summary\": \"Sammanfattning av kalenderhändelse här\",\n\"forceAllDay\": false,\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nHär är texten: \"{%selected_text%}\"" + "message": "Extrahera all relevant information som krävs för att generera en kalenderhändelse från följande text. Den extraherade informationen bör innehålla:\n- Händelsetitel\n- Startdatum och tid (inklusive tidszon, om angiven)\n- Slutdatum och tid (inklusive tidszon, om angiven)\n- Heldag (om angiven)\n- Deltagare\nSe till att informationen är tydligt och konsekvent formaterad så att den kan användas direkt för att skapa en kalenderhändelse.\nOm det finns relativa tidsreferenser, tänk på att datum och tid för e-postmeddelandet är \"{%mail_datetime%}\". Beräkna startdatum och tid baserat på denna referens. Om det beräknade startdatumet och tiden är tidigare än \"{%current_datetime%}\", beräkna om startdatumet och tiden med \"{%current_datetime%}\" som bas.\nOm varaktigheten inte anges, sätt den till en timme.\nDessa är deltagarna: {%author%}, {%recipients%}, {%cc_list%}. Om det finns, exkludera min adress: {%account_email_address%}.\nOm du inte kan få en eller flera av de obligatoriska uppgifterna, vänligen svara med en tom sträng.\nGenerera ett svar endast i JSON-format. Inkludera inte ytterligare text eller förklaringar; ange endast JSON. Här är formatet som ska användas:\n{\n\"startDate\": \"ÅÅÅÅMMDDTHHMMSS\",\n\"endDate\": \"ÅÅÅÅMMDDTHHMMSS\",\n\"summary\": \"Sammanfattning av kalenderhändelse här\",\n\"forceAllDay\": false,\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nHär är texten: \"{%mail_text_body_or_selected%}\"" }, "prompt_get_task_full_text": { "message": "Extrahera alla relevanta detaljer som krävs för att generera en uppgift från följande text. Den extraherade informationen bör innehålla:\n- Förfallodatum och tid (inklusive tidszon, om angiven)\n- Uppgiftssammanfattning\n- Initialt datum och tid (inklusive tidszon, om angiven)\nSe till att informationen är tydligt och konsekvent formaterad så att den kan användas direkt för att skapa en uppgift.\nOm det finns relativa tidsreferenser, tänk på att datum och tid för e-postmeddelandet är \"{%mail_datetime%}\". Beräkna startdatum och tid baserat på denna referens. Om det beräknade startdatumet och tiden är tidigare än \"{%current_datetime%}\", beräkna om startdatumet och tiden med \"{%current_datetime%}\" som bas.\nOm du inte kan få en eller flera av de obligatoriska uppgifterna, vänligen svara med en tom sträng.\nGenerera ett svar endast i JSON-format. Inkludera inte ytterligare text eller förklaringar; ange endast JSON. Här är formatet som ska användas:\n{\n\"InitialDate\": \"ÅÅÅÅMMDDTHHMMSS\",\n\"dueDate\": \"ÅÅÅÅMMDDTHHMMSS\",\n\"summary\": \"Uppgiftssammanfattning här\"\n}\nOm det inte finns någon information om datumen, ta bort dem.\nHär är texten: \"{%selected_text%}\"" From 954f1e3d8ac02b9fca84a09b0aefdd34f8822399 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 18:59:19 +0100 Subject: [PATCH 17/51] Translated using Weblate (French) Currently translated at 92.4% (426 of 461 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/fr/ --- _locales/fr/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_locales/fr/messages.json b/_locales/fr/messages.json index c03ffae9..6a3ef008 100644 --- a/_locales/fr/messages.json +++ b/_locales/fr/messages.json @@ -697,7 +697,7 @@ "message": "Ajouter un nouvel événement au calendrier" }, "prompt_get_calendar_event_full_text": { - "message": "Extrayez tous les détails pertinents nécessaires pour générer un événement de calendrier à partir du texte suivant. Les informations extraites doivent inclure :\n- Titre de l'événement\n- Date et heure de début (y compris le fuseau horaire, si spécifié)\n- Date et heure de fin (y compris le fuseau horaire, si spécifié)\n- Journée entière (si mentionnée)\n- Participants\nAssurez-vous que les données sont formatées clairement et de manière cohérente afin qu'elles puissent être utilisées directement pour créer un événement de calendrier.\nS'il y a des références temporelles relatives, considérez que la date et l'heure de l'email sont \"{%mail_datetime%}\". Calculez la date et l'heure de début sur cette base. Si la date et l'heure de début calculées sont antérieures à \"{%current_datetime%}\", recalculez-les en utilisant \"{%current_datetime%}\" comme base.\nSi la durée n'est pas spécifiée, définissez-la sur une heure.\nVoici les participants : {%author%}, {%recipients%}, {%cc_list%}. Si elle est présente, exclure mon adresse : {%account_email_address%}.\nSi vous n'êtes pas en mesure d'obtenir une ou plusieurs des informations requises, répondez avec une chaîne vide.\nGénérez une réponse uniquement au format JSON. N'incluez aucun texte ou explication supplémentaire ; fournissez uniquement le JSON. Voici le format à utiliser :\n{\n\"startDate\" : \"YYYYMMDDTHHMMSS\",\n\"endDate\" : \"YYYYMMDDTHHMMSS\",\n\"summary\" : \"Résumé de l'événement du calendrier ici\",\n\"forceAllDay\" : false\n\"attendees\" : [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nVoici le texte : \"{%selected_text%}\"" + "message": "Extrayez tous les détails pertinents nécessaires pour générer un événement de calendrier à partir du texte suivant. Les informations extraites doivent inclure :\n- Titre de l'événement\n- Date et heure de début (y compris le fuseau horaire, si spécifié)\n- Date et heure de fin (y compris le fuseau horaire, si spécifié)\n- Journée entière (si mentionnée)\n- Participants\nAssurez-vous que les données sont formatées clairement et de manière cohérente afin qu'elles puissent être utilisées directement pour créer un événement de calendrier.\nS'il y a des références temporelles relatives, considérez que la date et l'heure de l'email sont \"{%mail_datetime%}\". Calculez la date et l'heure de début sur cette base. Si la date et l'heure de début calculées sont antérieures à \"{%current_datetime%}\", recalculez-les en utilisant \"{%current_datetime%}\" comme base.\nSi la durée n'est pas spécifiée, définissez-la sur une heure.\nVoici les participants : {%author%}, {%recipients%}, {%cc_list%}. Si elle est présente, exclure mon adresse : {%account_email_address%}.\nSi vous n'êtes pas en mesure d'obtenir une ou plusieurs des informations requises, répondez avec une chaîne vide.\nGénérez une réponse uniquement au format JSON. N'incluez aucun texte ou explication supplémentaire ; fournissez uniquement le JSON. Voici le format à utiliser :\n{\n\"startDate\" : \"YYYYMMDDTHHMMSS\",\n\"endDate\" : \"YYYYMMDDTHHMMSS\",\n\"summary\" : \"Résumé de l'événement du calendrier ici\",\n\"forceAllDay\" : false\n\"attendees\" : [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nVoici le texte : \"{%mail_text_body_or_selected%}\"" }, "prefs_OptionText_get_calendar_event": { "message": "Ajouter un nouvel événement au calendrier à partir du texte sélectionné" From 2a04c1af1feb9393d9224433aabbe05c97b5dae1 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 18:59:35 +0100 Subject: [PATCH 18/51] Translated using Weblate (Chinese (Simplified Han script)) Currently translated at 62.6% (289 of 461 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/zh_Hans/ --- _locales/zh_Hans/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_locales/zh_Hans/messages.json b/_locales/zh_Hans/messages.json index deab75b7..5c187541 100644 --- a/_locales/zh_Hans/messages.json +++ b/_locales/zh_Hans/messages.json @@ -765,7 +765,7 @@ "message": "管理垃圾邮件过滤器设置" }, "prompt_get_calendar_event_full_text": { - "message": "从以下文本中提取生成日历事件所需的所有相关细节。提取的信息应包括:\n- 事件标题\n- 开始日期和时间(如果指定时区,则包括时区)\n- 结束日期和时间(如果指定时区,则包括时区)\n- 全天事件(如果提及)\n- 参与者 \n确保数据格式清晰且一致,以便可以直接用于创建日历事件。\n如果存在相对时间的引用,请注意邮件的日期和时间为“{%mail_datetime%}”。基于此参考计算开始日期和时间。如果计算出的开始日期和时间早于“{%current_datetime%}”,则使用“{%current_datetime%}”作为基准重新计算开始日期和时间。\n如果未指定持续时间,请将其设置为一小时。\n以下是参与者:{%author%}, {%recipients%}, {%cc_list%}。如有,请排除我的地址:{%account_email_address%}。\n如果无法获取一个或多个所需信息,请以空字符串响应。\n仅以 JSON 格式生成响应。不要包含任何额外的文本或说明,仅提供 JSON。以下是使用的格式:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"日历事件摘要\",\n\"forceAllDay\": false\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\n以下是文本:“{%selected_text%}”" + "message": "从以下文本中提取生成日历事件所需的所有相关细节。提取的信息应包括:\n- 事件标题\n- 开始日期和时间(如果指定时区,则包括时区)\n- 结束日期和时间(如果指定时区,则包括时区)\n- 全天事件(如果提及)\n- 参与者 \n确保数据格式清晰且一致,以便可以直接用于创建日历事件。\n如果存在相对时间的引用,请注意邮件的日期和时间为“{%mail_datetime%}”。基于此参考计算开始日期和时间。如果计算出的开始日期和时间早于“{%current_datetime%}”,则使用“{%current_datetime%}”作为基准重新计算开始日期和时间。\n如果未指定持续时间,请将其设置为一小时。\n以下是参与者:{%author%}, {%recipients%}, {%cc_list%}。如有,请排除我的地址:{%account_email_address%}。\n如果无法获取一个或多个所需信息,请以空字符串响应。\n仅以 JSON 格式生成响应。不要包含任何额外的文本或说明,仅提供 JSON。以下是使用的格式:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"日历事件摘要\",\n\"forceAllDay\": false\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\n以下是文本:“{%mail_text_body_or_selected%}”" }, "prefs_OptionText_get_calendar_event": { "message": "从所选文本添加新日历事件" From 3cf8d1a804d4e3ded9937b96eee889244ebb2b89 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 18:59:32 +0100 Subject: [PATCH 19/51] Translated using Weblate (Japanese) Currently translated at 92.4% (426 of 461 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/ja/ --- _locales/ja/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_locales/ja/messages.json b/_locales/ja/messages.json index fc4d9eb1..07746075 100644 --- a/_locales/ja/messages.json +++ b/_locales/ja/messages.json @@ -750,7 +750,7 @@ "message": "新しいカレンダーイベントを追加" }, "prompt_get_calendar_event_full_text": { - "message": "以下のテキストからカレンダーイベントを生成するために必要なすべての関連詳細を抽出してください。抽出情報には以下を含めてください:\n- イベントタイトル\n- 開始日時(指定されている場合はタイムゾーンを含む)\n- 終了日時(指定されている場合はタイムゾーンを含む)\n- 終日(言及されている場合)\n- 出席者\nデータはカレンダーイベントの作成に直接使用できるよう、明確かつ一貫した形式にしてください。\n相対的な時間参照がある場合、メールの日時は「{%mail_datetime%}」であることを考慮してください。この参照に基づいて開始日時を計算してください。計算された開始日時が「{%current_datetime%}」より前の場合、「{%current_datetime%}」を基準として開始日時を再計算してください。\n期間が指定されていない場合は、1時間に設定してください。\n出席者:{%author%}、{%recipients%}、{%cc_list%}。存在する場合、私のアドレス{%account_email_address%}を除外してください。\n必要な情報の1つ以上を取得できない場合は、空の文字列で応答してください。\nJSON形式のみで応答を生成してください。追加のテキストや説明は含めず、JSONのみを提供してください。使用する形式:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"カレンダーイベントの概要をここに\",\n\"forceAllDay\": false,\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nテキスト:「{%selected_text%}」" + "message": "以下のテキストからカレンダーイベントを生成するために必要なすべての関連詳細を抽出してください。抽出情報には以下を含めてください:\n- イベントタイトル\n- 開始日時(指定されている場合はタイムゾーンを含む)\n- 終了日時(指定されている場合はタイムゾーンを含む)\n- 終日(言及されている場合)\n- 出席者\nデータはカレンダーイベントの作成に直接使用できるよう、明確かつ一貫した形式にしてください。\n相対的な時間参照がある場合、メールの日時は「{%mail_datetime%}」であることを考慮してください。この参照に基づいて開始日時を計算してください。計算された開始日時が「{%current_datetime%}」より前の場合、「{%current_datetime%}」を基準として開始日時を再計算してください。\n期間が指定されていない場合は、1時間に設定してください。\n出席者:{%author%}、{%recipients%}、{%cc_list%}。存在する場合、私のアドレス{%account_email_address%}を除外してください。\n必要な情報の1つ以上を取得できない場合は、空の文字列で応答してください。\nJSON形式のみで応答を生成してください。追加のテキストや説明は含めず、JSONのみを提供してください。使用する形式:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"カレンダーイベントの概要をここに\",\n\"forceAllDay\": false,\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nテキスト:「{%mail_text_body_or_selected%}」" }, "prompt_get_task": { "message": "新しいタスクを追加" From 4bf501682472c2d81f86cbb2f13e95fe2b165889 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 18:59:08 +0100 Subject: [PATCH 20/51] Translated using Weblate (Polish) Currently translated at 61.8% (285 of 461 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/pl/ --- _locales/pl/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_locales/pl/messages.json b/_locales/pl/messages.json index c55f6d1d..b60db714 100644 --- a/_locales/pl/messages.json +++ b/_locales/pl/messages.json @@ -688,7 +688,7 @@ "message": "Dodaj nowe wydarzenie w kalendarzu" }, "prompt_get_calendar_event_full_text": { - "message": "Wyodrębnij wszystkie istotne szczegóły wymagane do wygenerowania wydarzenia w kalendarzu z poniższego tekstu. Wyodrębnione informacje powinny obejmować:\n- Tytuł wydarzenia\n- Datę i godzinę rozpoczęcia (w tym strefę czasową, jeśli została określona)\n- Datę i godzinę zakończenia (w tym strefę czasową, jeśli została określona)\n- Cały dzień (jeśli jest podany)\n- Uczestnicy\nUpewnij się, że dane są sformatowane w sposób jasny i spójny, tak aby można go bezpośrednio wykorzystać do utworzenia wydarzenia w kalendarzu.\nJeśli istnieją odniesienia do czasu względnego, pamiętaj, że data i godzina wysłania wiadomości e-mail to „{%mail_datetime%}”. Oblicz datę i godzinę rozpoczęcia na podstawie tego odniesienia. Jeśli obliczona data i godzina rozpoczęcia są wcześniejsze niż „{%current_datetime%}”, oblicz ponownie datę i godzinę rozpoczęcia, stosując jako podstawę „{%current_datetime%}”.\nJeśli czas trwania nie jest określony, ustaw go na jedną godzinę.\nOto uczestnicy: {%author%}, {%recipients%}, {%cc_list%}. Jeśli jest obecny, wyklucz mój adres: {%account_email_address%}.\nJeśli nie możesz uzyskać co najmniej jednej z wymaganych informacji, w odpowiedzi wpisz pusty ciąg znaków.\nWygeneruj odpowiedź tylko w formacie JSON. Nie dołączaj żadnego dodatkowego tekstu ani wyjaśnień; podaj tylko JSON. Oto format, którego należy użyć:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"Tutaj podsumowanie wydarzenia w kalendarzu\",\n\"forceAllDay\": false\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nOto tekst:\"{%selected_text %}\"" + "message": "Wyodrębnij wszystkie istotne szczegóły wymagane do wygenerowania wydarzenia w kalendarzu z poniższego tekstu. Wyodrębnione informacje powinny obejmować:\n- Tytuł wydarzenia\n- Datę i godzinę rozpoczęcia (w tym strefę czasową, jeśli została określona)\n- Datę i godzinę zakończenia (w tym strefę czasową, jeśli została określona)\n- Cały dzień (jeśli jest podany)\n- Uczestnicy\nUpewnij się, że dane są sformatowane w sposób jasny i spójny, tak aby można go bezpośrednio wykorzystać do utworzenia wydarzenia w kalendarzu.\nJeśli istnieją odniesienia do czasu względnego, pamiętaj, że data i godzina wysłania wiadomości e-mail to „{%mail_datetime%}”. Oblicz datę i godzinę rozpoczęcia na podstawie tego odniesienia. Jeśli obliczona data i godzina rozpoczęcia są wcześniejsze niż „{%current_datetime%}”, oblicz ponownie datę i godzinę rozpoczęcia, stosując jako podstawę „{%current_datetime%}”.\nJeśli czas trwania nie jest określony, ustaw go na jedną godzinę.\nOto uczestnicy: {%author%}, {%recipients%}, {%cc_list%}. Jeśli jest obecny, wyklucz mój adres: {%account_email_address%}.\nJeśli nie możesz uzyskać co najmniej jednej z wymaganych informacji, w odpowiedzi wpisz pusty ciąg znaków.\nWygeneruj odpowiedź tylko w formacie JSON. Nie dołączaj żadnego dodatkowego tekstu ani wyjaśnień; podaj tylko JSON. Oto format, którego należy użyć:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"Tutaj podsumowanie wydarzenia w kalendarzu\",\n\"forceAllDay\": false\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nOto tekst:\"{%mail_text_body_or_selected%}\"" }, "prefs_OptionText_get_calendar_event": { "message": "Dodaj nowe wydarzenie w kalendarzu z zaznaczonego tekstu" From 6b9ad17d7c8e1c6fe0daefe8edab79c5f3f0a442 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 18:59:08 +0100 Subject: [PATCH 21/51] Translated using Weblate (Russian) Currently translated at 85.0% (392 of 461 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/ru/ --- _locales/ru/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_locales/ru/messages.json b/_locales/ru/messages.json index a4350b2b..31b4fe78 100644 --- a/_locales/ru/messages.json +++ b/_locales/ru/messages.json @@ -735,7 +735,7 @@ "message": "Добавьте новое событие календаря" }, "prompt_get_calendar_event_full_text": { - "message": "Извлеките из следующего текста все необходимые сведения, необходимые для создания календарного события. Извлеченная информация должна включать:\n- Название события\n- Дата и время начала (включая часовой пояс, если он указан)\n- Дата и время окончания (включая часовой пояс, если он указан)\n- Полный день (если указано)\n- Участники\nУбедитесь, что данные отформатированы четко и последовательно, чтобы их можно было напрямую использовать для создания календарного события.\nЕсли есть относительные временные ссылки, считайте, что дата и время письма - это \"{%mail_datetime%}\". Рассчитайте дату и время начала на основе этой ссылки. Если вычисленные дата и время начала раньше, чем \"{%current_datetime%}\", пересчитайте дату и время начала, взяв за основу \"{%current_datetime%}\".\nЕсли продолжительность не указана, установите ее равной одному часу.\nК ним относятся: {%author%}, {%recipients%}, {%cc_list%}. Если присутствует, исключите мой адрес: {%account_email_address%}.\nЕсли вы не можете получить одну или несколько требуемых данных, ответьте пустой строкой.\nГенерируйте ответ только в формате JSON. Не включайте никаких дополнительных текстов или пояснений; предоставляйте только JSON. Вот формат, который следует использовать:\n{\n\"startDate\": \"ГГГГММДДДХММССС\",\n\"endDate\": \"ГГГГММДДДХММССС\",\n\"summary\": \"Здесь выводится краткое описание события календаря\",\n\"forceAllDay\": false,\n\"attendees\": [участник1@example.com,участник2@example.com,участник3@example.com]\n}\nВот текст: \"{%selected_text%}\"" + "message": "Извлеките из следующего текста все необходимые сведения, необходимые для создания календарного события. Извлеченная информация должна включать:\n- Название события\n- Дата и время начала (включая часовой пояс, если он указан)\n- Дата и время окончания (включая часовой пояс, если он указан)\n- Полный день (если указано)\n- Участники\nУбедитесь, что данные отформатированы четко и последовательно, чтобы их можно было напрямую использовать для создания календарного события.\nЕсли есть относительные временные ссылки, считайте, что дата и время письма - это \"{%mail_datetime%}\". Рассчитайте дату и время начала на основе этой ссылки. Если вычисленные дата и время начала раньше, чем \"{%current_datetime%}\", пересчитайте дату и время начала, взяв за основу \"{%current_datetime%}\".\nЕсли продолжительность не указана, установите ее равной одному часу.\nК ним относятся: {%author%}, {%recipients%}, {%cc_list%}. Если присутствует, исключите мой адрес: {%account_email_address%}.\nЕсли вы не можете получить одну или несколько требуемых данных, ответьте пустой строкой.\nГенерируйте ответ только в формате JSON. Не включайте никаких дополнительных текстов или пояснений; предоставляйте только JSON. Вот формат, который следует использовать:\n{\n\"startDate\": \"ГГГГММДДДХММССС\",\n\"endDate\": \"ГГГГММДДДХММССС\",\n\"summary\": \"Здесь выводится краткое описание события календаря\",\n\"forceAllDay\": false,\n\"attendees\": [участник1@example.com,участник2@example.com,участник3@example.com]\n}\nВот текст: \"{%mail_text_body_or_selected%}\"" }, "prompt_get_task": { "message": "Добавить новую задачу" From 4b5768c99061f80b3a537c310214d393d4f072af Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 18:56:57 +0100 Subject: [PATCH 22/51] Translated using Weblate (Italian) Currently translated at 92.4% (426 of 461 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/it/ --- _locales/it/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_locales/it/messages.json b/_locales/it/messages.json index 158a65e4..25ed2bf6 100644 --- a/_locales/it/messages.json +++ b/_locales/it/messages.json @@ -697,7 +697,7 @@ "message": "Aggiungi un nuovo evento al calendario" }, "prompt_get_calendar_event_full_text": { - "message": "Estrai tutti i dettagli rilevanti necessari per generare un evento del calendario dal seguente testo. Le informazioni estratte devono includere:\n- Titolo dell'evento\n- Data e ora di inizio (incluso il fuso orario, se specificato)\n- Data e ora di fine (incluso il fuso orario, se specificato)\n- Giornata intera (se menzionato)\n- I partecipanti\nAssicurati che i dati siano formattati in modo chiaro e coerente in modo che possano essere utilizzati direttamente per creare un evento del calendario.\nSe ci sono riferimenti temporali relativi, considera che la data e l'ora dell'email sono \"{%mail_datetime%}\". Calcola la data e l'ora di inizio in base a questo riferimento. Se la data e l'ora di inizio calcolate sono precedenti a \"{%current_datetime%}\", ricalcolale utilizzando \"{%current_datetime%}\" come base.\nSe la durata non è specificata, impostala a un'ora.\nQuesti sono i partecipanti: {%author%}, {%recipients%}, {%cc_list%}. Se presente, escludi il mio indirizzo: {%account_email_address%}.\nSe non riesci a ottenere una o più delle informazioni richieste, rispondi con una stringa vuota.\nGenera una risposta solo in formato JSON. Non includere testo o spiegazioni aggiuntive; fornisci solo il JSON. Ecco il formato da utilizzare:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"Riassunto evento calendario qui\",\n\"forceAllDay\": false\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nEcco il testo: \"{%selected_text%}\"" + "message": "Estrai tutti i dettagli rilevanti necessari per generare un evento del calendario dal seguente testo. Le informazioni estratte devono includere:\n- Titolo dell'evento\n- Data e ora di inizio (incluso il fuso orario, se specificato)\n- Data e ora di fine (incluso il fuso orario, se specificato)\n- Giornata intera (se menzionato)\n- I partecipanti\nAssicurati che i dati siano formattati in modo chiaro e coerente in modo che possano essere utilizzati direttamente per creare un evento del calendario.\nSe ci sono riferimenti temporali relativi, considera che la data e l'ora dell'email sono \"{%mail_datetime%}\". Calcola la data e l'ora di inizio in base a questo riferimento. Se la data e l'ora di inizio calcolate sono precedenti a \"{%current_datetime%}\", ricalcolale utilizzando \"{%current_datetime%}\" come base.\nSe la durata non è specificata, impostala a un'ora.\nQuesti sono i partecipanti: {%author%}, {%recipients%}, {%cc_list%}. Se presente, escludi il mio indirizzo: {%account_email_address%}.\nSe non riesci a ottenere una o più delle informazioni richieste, rispondi con una stringa vuota.\nGenera una risposta solo in formato JSON. Non includere testo o spiegazioni aggiuntive; fornisci solo il JSON. Ecco il formato da utilizzare:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"Riassunto evento calendario qui\",\n\"forceAllDay\": false\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nEcco il testo: \"{%mail_text_body_or_selected%}\"" }, "prefs_OptionText_get_calendar_event": { "message": "Aggiungi un nuovo evento al calendario dal testo selezionato" From 9edae00b916dcdf937fbc233113c613464fb4e4a Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 18:59:40 +0100 Subject: [PATCH 23/51] Translated using Weblate (Chinese (Traditional Han script)) Currently translated at 86.1% (397 of 461 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/zh_Hant/ --- _locales/zh_Hant/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_locales/zh_Hant/messages.json b/_locales/zh_Hant/messages.json index dd60ee03..50827707 100644 --- a/_locales/zh_Hant/messages.json +++ b/_locales/zh_Hant/messages.json @@ -1075,7 +1075,7 @@ "message": "您尚未選擇 ChatGPT API 的模型。請在選項頁面中選擇一個。" }, "prompt_get_calendar_event_full_text": { - "message": "從以下文字中提取所有需要生成日曆事件的相關資訊。 提取的資訊應包含:\n- 事件名稱\n- 起始日期和時間(包含時區,如果指定)\n- 結束日期和時間(包含時間區,如果指定)\n- 整天事件(如果提及\n- 參與者\n確保數據以清晰一致的方式格式化,以便直接用於創建日曆事件。\n如果有相對時間參考,請考慮電子郵件的日期和時間是 「{%mail_datetime%}」。 計算基於此參考的起日期和時間。 如果計算出的起日期和時間早於「{%current_datetime%}」,則使用「{%current_datetime%}」作為基準重新計算起日期和時間。\n如果持續時間沒有指定,則設定為一小時。\n參與者:{%author%}, {%recipients%}, {%cc_list%}。如果存在,請排除我的地址:{%account_email_address%}。\n如果無法獲得其中一個或多個所需的資訊,請回覆一個空字串。\n請以 JSON 格式回覆,不要包含任何額外的文字或說明,提供僅 JSON。 以下是將要使用的格式:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"日曆事件摘要在此\",\n\"forceAllDay\": false,\n\"attendees\": [\"attendee1@example.com \",\"attendee2@example.com \",\"attendee3@example.com \"]\n}\n這裡是文字:「{%selected_text%}」" + "message": "從以下文字中提取所有需要生成日曆事件的相關資訊。 提取的資訊應包含:\n- 事件名稱\n- 起始日期和時間(包含時區,如果指定)\n- 結束日期和時間(包含時間區,如果指定)\n- 整天事件(如果提及\n- 參與者\n確保數據以清晰一致的方式格式化,以便直接用於創建日曆事件。\n如果有相對時間參考,請考慮電子郵件的日期和時間是 「{%mail_datetime%}」。 計算基於此參考的起日期和時間。 如果計算出的起日期和時間早於「{%current_datetime%}」,則使用「{%current_datetime%}」作為基準重新計算起日期和時間。\n如果持續時間沒有指定,則設定為一小時。\n參與者:{%author%}, {%recipients%}, {%cc_list%}。如果存在,請排除我的地址:{%account_email_address%}。\n如果無法獲得其中一個或多個所需的資訊,請回覆一個空字串。\n請以 JSON 格式回覆,不要包含任何額外的文字或說明,提供僅 JSON。 以下是將要使用的格式:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"日曆事件摘要在此\",\n\"forceAllDay\": false,\n\"attendees\": [\"attendee1@example.com \",\"attendee2@example.com \",\"attendee3@example.com \"]\n}\n這裡是文字:「{%mail_text_body_or_selected%}」" }, "TranslateText": { "message": "您願意幫忙翻譯這個附加元件嗎?" From 62ab34526ccbb4b9e0380ea7bf5a16293cdf41fe Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 18:59:47 +0100 Subject: [PATCH 24/51] Translated using Weblate (Czech) Currently translated at 78.5% (362 of 461 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/cs/ --- _locales/cs/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_locales/cs/messages.json b/_locales/cs/messages.json index 1ab9589e..faa6b283 100644 --- a/_locales/cs/messages.json +++ b/_locales/cs/messages.json @@ -795,7 +795,7 @@ "message": "Spravovat nastavení štítků" }, "prompt_get_calendar_event_full_text": { - "message": "Z následujícího textu extrahuj všechny relevantní podrobnosti potřebné k vygenerování události kalendáře. Extrahované informace by měly zahrnovat:\n- Název události\n- Datum a čas zahájení (včetně časového pásma, pokud je uvedeno)\n- Datum a čas ukončení (včetně časového pásma, pokud je uvedeno)\n- Celý den (pokud je zmíněn)\n- Účastníci\nZajisti, aby byla data formátována jasně a konzistentně, aby je bylo možné přímo použít k vytvoření události kalendáře.\nPokud existují relativní časové odkazy, vezmi v úvahu, že datum a čas e-mailu jsou \"{%mail_datetime%}\". Vypočítej datum a čas zahájení na základě tohoto odkazu. Pokud jsou vypočítané datum a čas zahájení dřívější než \"{%current_datetime%}\", přepočti datum a čas zahájení pomocí \"{%current_datetime%}\" jako základu.\nPokud není zadána doba trvání, nastav ji na jednu hodinu.\nToto jsou účastníci: {%author%}, {%recipients%}, {%cc_list%}. Pokud je přítomna, nezahrnuj mou adresu: {%account_email_address%}.\nPokud nejsi schopen získat jednu nebo více požadovaných informací, odpověz prázdným řetězcem.\nVygeneruj odpověď pouze ve formátu JSON. Nezahrnuj žádný další text ani vysvětlení; poskytni pouze JSON. Zde je formát, který se má použít:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"Zde je souhrn události kalendáře\",\n\"forceAllDay\": false,\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nZde je text:\"{%selected_text%}\"" + "message": "Z následujícího textu extrahuj všechny relevantní podrobnosti potřebné k vygenerování události kalendáře. Extrahované informace by měly zahrnovat:\n- Název události\n- Datum a čas zahájení (včetně časového pásma, pokud je uvedeno)\n- Datum a čas ukončení (včetně časového pásma, pokud je uvedeno)\n- Celý den (pokud je zmíněn)\n- Účastníci\nZajisti, aby byla data formátována jasně a konzistentně, aby je bylo možné přímo použít k vytvoření události kalendáře.\nPokud existují relativní časové odkazy, vezmi v úvahu, že datum a čas e-mailu jsou \"{%mail_datetime%}\". Vypočítej datum a čas zahájení na základě tohoto odkazu. Pokud jsou vypočítané datum a čas zahájení dřívější než \"{%current_datetime%}\", přepočti datum a čas zahájení pomocí \"{%current_datetime%}\" jako základu.\nPokud není zadána doba trvání, nastav ji na jednu hodinu.\nToto jsou účastníci: {%author%}, {%recipients%}, {%cc_list%}. Pokud je přítomna, nezahrnuj mou adresu: {%account_email_address%}.\nPokud nejsi schopen získat jednu nebo více požadovaných informací, odpověz prázdným řetězcem.\nVygeneruj odpověď pouze ve formátu JSON. Nezahrnuj žádný další text ani vysvětlení; poskytni pouze JSON. Zde je formát, který se má použít:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"Zde je souhrn události kalendáře\",\n\"forceAllDay\": false,\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nZde je text:\"{%mail_text_body_or_selected%}\"" }, "prefs_OptionText_get_calendar_event": { "message": "Přidat novou událost do kalendáře z vybraného textu" From c9820bbb56cce406b6b4919f3404182cc1d7ca1c Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 18:58:53 +0100 Subject: [PATCH 25/51] Translated using Weblate (Spanish) Currently translated at 92.4% (426 of 461 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/es/ --- _locales/es/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_locales/es/messages.json b/_locales/es/messages.json index 1db8b9bf..e0c082c6 100644 --- a/_locales/es/messages.json +++ b/_locales/es/messages.json @@ -744,7 +744,7 @@ "message": "Agregar un nuevo evento de calendario" }, "prompt_get_calendar_event_full_text": { - "message": "Extrae todos los detalles relevantes necesarios para generar un evento de calendario a partir del siguiente texto. La información extraída debe incluir:\n- Título del evento\n- Fecha y hora de inicio (incluyendo zona horaria, si se especifica)\n- Fecha y hora de fin (incluyendo zona horaria, si se especifica)\n- Día completo (si se menciona)\n- Asistentes\nAsegúrate de que los datos estén formateados de manera clara y consistente para que puedan usarse directamente en la creación de un evento de calendario.\nSi hay referencias de tiempo relativas, considera que la fecha y hora del correo son \"{%mail_datetime%}\". Calcula la fecha y hora de inicio basándote en esta referencia. Si la fecha y hora de inicio calculadas son anteriores a \"{%current_datetime%}\", recalcula la fecha y hora de inicio usando \"{%current_datetime%}\" como base.\nSi la duración no se especifica, establécela en una hora.\nEstos son los asistentes: {%author%}, {%recipients%}, {%cc_list%}. Si están presentes, excluye mi dirección: {%account_email_address%}.\nSi no puedes obtener uno o más de los datos requeridos, responde con una cadena vacía.\nGenera una respuesta solo en formato JSON. No incluyas texto adicional ni explicaciones; proporciona únicamente el JSON. El formato a usar es:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"Resumen del evento de calendario aquí\",\n\"forceAllDay\": false,\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nEste es el texto:\"{%selected_text%}\"" + "message": "Extrae todos los detalles relevantes necesarios para generar un evento de calendario a partir del siguiente texto. La información extraída debe incluir:\n- Título del evento\n- Fecha y hora de inicio (incluyendo zona horaria, si se especifica)\n- Fecha y hora de fin (incluyendo zona horaria, si se especifica)\n- Día completo (si se menciona)\n- Asistentes\nAsegúrate de que los datos estén formateados de manera clara y consistente para que puedan usarse directamente en la creación de un evento de calendario.\nSi hay referencias de tiempo relativas, considera que la fecha y hora del correo son \"{%mail_datetime%}\". Calcula la fecha y hora de inicio basándote en esta referencia. Si la fecha y hora de inicio calculadas son anteriores a \"{%current_datetime%}\", recalcula la fecha y hora de inicio usando \"{%current_datetime%}\" como base.\nSi la duración no se especifica, establécela en una hora.\nEstos son los asistentes: {%author%}, {%recipients%}, {%cc_list%}. Si están presentes, excluye mi dirección: {%account_email_address%}.\nSi no puedes obtener uno o más de los datos requeridos, responde con una cadena vacía.\nGenera una respuesta solo en formato JSON. No incluyas texto adicional ni explicaciones; proporciona únicamente el JSON. El formato a usar es:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"Resumen del evento de calendario aquí\",\n\"forceAllDay\": false,\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nEste es el texto:\"{%mail_text_body_or_selected%}\"" }, "prompt_get_task": { "message": "Agregar una nueva tarea" From 454789149f71586ab96fbd3b0a0a9cb6f2942ee9 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 18:59:15 +0100 Subject: [PATCH 26/51] Translated using Weblate (Portuguese (Brazil)) Currently translated at 58.7% (271 of 461 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/pt_BR/ --- _locales/pt-br/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_locales/pt-br/messages.json b/_locales/pt-br/messages.json index 9341402d..c6730a35 100644 --- a/_locales/pt-br/messages.json +++ b/_locales/pt-br/messages.json @@ -697,7 +697,7 @@ "message": "Adicionar um novo evento ao calendário" }, "prompt_get_calendar_event_full_text": { - "message": "Extraia todos os detalhes relevantes necessários para gerar um evento de calendário a partir do texto a seguir. As informações extraídas devem incluir:\n- Título do evento\n- Data e hora de início (incluindo fuso horário, se especificado)\n- Data e hora de término (incluindo fuso horário, se especificado)\n- Dia inteiro (se mencionado)\n- Participantes\nCertifique-se de que os dados estejam formatados de maneira clara e consistente para que possam ser usados diretamente para criar um evento de calendário.\nSe houver referências de tempo relativas, considere que a data e a hora do e-mail são \"{%mail_datetime%}\". Calcule a data e a hora de início com base nessa referência. Se a data e a hora de início calculadas forem anteriores a \"{%current_datetime%}\", recalcule a data e a hora de início usando \"{%current_datetime%}\" como base.\nSe a duração não for especificada, defina-a como uma hora.\nEstes são os participantes: {%author%}, {%recipients%}, {%cc_list%}. Se estiver presente, exclua meu endereço: {%account_email_address%}.\nSe você não conseguir obter uma ou mais informações necessárias, responda com uma string vazia.\nGere uma resposta apenas no formato JSON. Não inclua texto ou explicações adicionais; forneça apenas o JSON. Aqui está o formato a ser usado:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"Resumo do evento do calendário aqui\",\n\"forceAllDay\": false\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nAqui está o texto: \"{%selected_text%}\"" + "message": "Extraia todos os detalhes relevantes necessários para gerar um evento de calendário a partir do texto a seguir. As informações extraídas devem incluir:\n- Título do evento\n- Data e hora de início (incluindo fuso horário, se especificado)\n- Data e hora de término (incluindo fuso horário, se especificado)\n- Dia inteiro (se mencionado)\n- Participantes\nCertifique-se de que os dados estejam formatados de maneira clara e consistente para que possam ser usados diretamente para criar um evento de calendário.\nSe houver referências de tempo relativas, considere que a data e a hora do e-mail são \"{%mail_datetime%}\". Calcule a data e a hora de início com base nessa referência. Se a data e a hora de início calculadas forem anteriores a \"{%current_datetime%}\", recalcule a data e a hora de início usando \"{%current_datetime%}\" como base.\nSe a duração não for especificada, defina-a como uma hora.\nEstes são os participantes: {%author%}, {%recipients%}, {%cc_list%}. Se estiver presente, exclua meu endereço: {%account_email_address%}.\nSe você não conseguir obter uma ou mais informações necessárias, responda com uma string vazia.\nGere uma resposta apenas no formato JSON. Não inclua texto ou explicações adicionais; forneça apenas o JSON. Aqui está o formato a ser usado:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"Resumo do evento do calendário aqui\",\n\"forceAllDay\": false\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nAqui está o texto: \"{%mail_text_body_or_selected%}\"" }, "prefs_OptionText_get_calendar_event": { "message": "Adicionar um novo evento ao calendário a partir do texto selecionado" From 816118ed3436d8f46ba8d375a1db4ffbeba3faf0 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 18:58:39 +0100 Subject: [PATCH 27/51] Translated using Weblate (German) Currently translated at 92.4% (426 of 461 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/de/ --- _locales/de/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_locales/de/messages.json b/_locales/de/messages.json index efd4f508..7515b3a0 100644 --- a/_locales/de/messages.json +++ b/_locales/de/messages.json @@ -697,7 +697,7 @@ "message": "Ein neues Kalenderevent hinzufügen" }, "prompt_get_calendar_event_full_text": { - "message": "Extrahieren Sie alle relevanten Details, die erforderlich sind, um ein Kalenderevent aus dem folgenden Text zu erstellen. Die extrahierten Informationen sollten Folgendes enthalten:\n- Ereignistitel\n- Startdatum und -uhrzeit (einschließlich Zeitzone, falls angegeben)\n- Enddatum und -uhrzeit (einschließlich Zeitzone, falls angegeben)\n- Ganztägig (falls erwähnt)\n- Teilnehmer\nStellen Sie sicher, dass die Daten klar und konsistent formatiert sind, sodass sie direkt für die Erstellung eines Kalenderevents verwendet werden können.\nFalls relative Zeitangaben enthalten sind, beachten Sie, dass Datum und Uhrzeit der E-Mail \"{%mail_datetime%}\" sind. Berechnen Sie das Startdatum und die Startzeit basierend auf diesem Bezugspunkt. Falls das berechnete Startdatum und die Startzeit vor \"{%current_datetime%}\" liegen, berechnen Sie Startdatum und -zeit erneut, wobei Sie \"{%current_datetime%}\" als Grundlage verwenden.\nFalls keine Dauer angegeben ist, setzen Sie sie auf eine Stunde.\nDas sind die Teilnehmer: {%author%}, {%recipients%}, {%cc_list%}. Falls vorhanden, meine Adresse ausschließen: {%account_email_address%}.\nFalls Sie eine oder mehrere der erforderlichen Informationen nicht erhalten können, antworten Sie mit einem leeren String.\nErstellen Sie eine Antwort ausschließlich im JSON-Format. Fügen Sie keinen zusätzlichen Text oder Erklärungen hinzu; geben Sie nur das JSON an. Verwenden Sie folgendes Format:\n{\n \"startDate\": \"YYYYMMDDTHHMMSS\",\n \"endDate\": \"YYYYMMDDTHHMMSS\",\n \"summary\": \"Zusammenfassung des Kalenderevents hier\",\n \"forceAllDay\": false\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nHier ist der Text: \"{%selected_text%}\"" + "message": "Extrahieren Sie alle relevanten Details, die erforderlich sind, um ein Kalenderevent aus dem folgenden Text zu erstellen. Die extrahierten Informationen sollten Folgendes enthalten:\n- Ereignistitel\n- Startdatum und -uhrzeit (einschließlich Zeitzone, falls angegeben)\n- Enddatum und -uhrzeit (einschließlich Zeitzone, falls angegeben)\n- Ganztägig (falls erwähnt)\n- Teilnehmer\nStellen Sie sicher, dass die Daten klar und konsistent formatiert sind, sodass sie direkt für die Erstellung eines Kalenderevents verwendet werden können.\nFalls relative Zeitangaben enthalten sind, beachten Sie, dass Datum und Uhrzeit der E-Mail \"{%mail_datetime%}\" sind. Berechnen Sie das Startdatum und die Startzeit basierend auf diesem Bezugspunkt. Falls das berechnete Startdatum und die Startzeit vor \"{%current_datetime%}\" liegen, berechnen Sie Startdatum und -zeit erneut, wobei Sie \"{%current_datetime%}\" als Grundlage verwenden.\nFalls keine Dauer angegeben ist, setzen Sie sie auf eine Stunde.\nDas sind die Teilnehmer: {%author%}, {%recipients%}, {%cc_list%}. Falls vorhanden, meine Adresse ausschließen: {%account_email_address%}.\nFalls Sie eine oder mehrere der erforderlichen Informationen nicht erhalten können, antworten Sie mit einem leeren String.\nErstellen Sie eine Antwort ausschließlich im JSON-Format. Fügen Sie keinen zusätzlichen Text oder Erklärungen hinzu; geben Sie nur das JSON an. Verwenden Sie folgendes Format:\n{\n \"startDate\": \"YYYYMMDDTHHMMSS\",\n \"endDate\": \"YYYYMMDDTHHMMSS\",\n \"summary\": \"Zusammenfassung des Kalenderevents hier\",\n \"forceAllDay\": false\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nHier ist der Text: \"{%mail_text_body_or_selected%}\"" }, "prefs_OptionText_get_calendar_event": { "message": "Ein neues Kalenderevent aus ausgewähltem Text hinzufügen" From f5d278183bcfcdd28c4e80b506bb3f17421e22d4 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 18:59:27 +0100 Subject: [PATCH 28/51] Translated using Weblate (Greek) Currently translated at 92.4% (426 of 461 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/el/ --- _locales/el/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_locales/el/messages.json b/_locales/el/messages.json index 20938494..c240bfd7 100644 --- a/_locales/el/messages.json +++ b/_locales/el/messages.json @@ -564,7 +564,7 @@ "message": "Προσθήκη νέου συμβάντος ημερολογίου" }, "prompt_get_calendar_event_full_text": { - "message": "Εξαγάγετε όλες τις σχετικές λεπτομέρειες που απαιτούνται για τη δημιουργία ενός συμβάντος ημερολογίου από το ακόλουθο κείμενο. Οι εξαγόμενες πληροφορίες θα πρέπει να περιλαμβάνουν:\n- Τίτλο συμβάντος\n- Ημερομηνία και ώρα έναρξης (συμπεριλαμβανομένης της ζώνης ώρας, εάν καθορίζεται)\n- Ημερομηνία και ώρα λήξης (συμπεριλαμβανομένης της ζώνης ώρας, εάν καθορίζεται)\n- Ολόκληρη ημέρα (εάν αναφέρεται)\n- Συμμετέχοντες\nΒεβαιωθείτε ότι τα δεδομένα έχουν μορφοποιηθεί με σαφήνεια και συνέπεια, ώστε να μπορούν να χρησιμοποιηθούν άμεσα για τη δημιουργία ενός συμβάντος ημερολογίου.\nΕάν υπάρχουν σχετικές χρονικές αναφορές, λάβετε υπόψη ότι η ημερομηνία και η ώρα του email είναι \"{%mail_datetime%}\". Υπολογίστε την ημερομηνία και την ώρα έναρξης με βάση αυτήν την αναφορά. Εάν η υπολογισμένη ημερομηνία και ώρα έναρξης είναι προγενέστερες από το \"{%current_datetime%}\", υπολογίστε ξανά την ημερομηνία και την ώρα έναρξης χρησιμοποιώντας το \"{%current_datetime%}\" ως βάση.\nΕάν η διάρκεια δεν έχει καθοριστεί, ορίστε την σε μία ώρα.\nΑυτοί είναι οι συμμετέχοντες: {%author%}, {%recipients%}, {%cc_list%}. Εάν υπάρχει, εξαιρέστε τη διεύθυνσή μου: {%account_email_address%}.\nΕάν δεν μπορείτε να λάβετε μία ή περισσότερες από τις απαιτούμενες πληροφορίες, απαντήστε με μια κενή συμβολοσειρά.\nΔημιουργήστε μια απάντηση μόνο σε μορφή JSON. Μην συμπεριλάβετε κανένα επιπλέον κείμενο ή εξηγήσεις. Δώστε μόνο το JSON. Η μορφή που θα χρησιμοποιηθεί είναι η εξής:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"Σύνοψη συμβάντος ημερολογίου εδώ\",\n\"forceAllDay\": false,\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nΤο κείμενο είναι: \"{%selected_text%}\"" + "message": "Εξαγάγετε όλες τις σχετικές λεπτομέρειες που απαιτούνται για τη δημιουργία ενός συμβάντος ημερολογίου από το ακόλουθο κείμενο. Οι εξαγόμενες πληροφορίες θα πρέπει να περιλαμβάνουν:\n- Τίτλο συμβάντος\n- Ημερομηνία και ώρα έναρξης (συμπεριλαμβανομένης της ζώνης ώρας, εάν καθορίζεται)\n- Ημερομηνία και ώρα λήξης (συμπεριλαμβανομένης της ζώνης ώρας, εάν καθορίζεται)\n- Ολόκληρη ημέρα (εάν αναφέρεται)\n- Συμμετέχοντες\nΒεβαιωθείτε ότι τα δεδομένα έχουν μορφοποιηθεί με σαφήνεια και συνέπεια, ώστε να μπορούν να χρησιμοποιηθούν άμεσα για τη δημιουργία ενός συμβάντος ημερολογίου.\nΕάν υπάρχουν σχετικές χρονικές αναφορές, λάβετε υπόψη ότι η ημερομηνία και η ώρα του email είναι \"{%mail_datetime%}\". Υπολογίστε την ημερομηνία και την ώρα έναρξης με βάση αυτήν την αναφορά. Εάν η υπολογισμένη ημερομηνία και ώρα έναρξης είναι προγενέστερες από το \"{%current_datetime%}\", υπολογίστε ξανά την ημερομηνία και την ώρα έναρξης χρησιμοποιώντας το \"{%current_datetime%}\" ως βάση.\nΕάν η διάρκεια δεν έχει καθοριστεί, ορίστε την σε μία ώρα.\nΑυτοί είναι οι συμμετέχοντες: {%author%}, {%recipients%}, {%cc_list%}. Εάν υπάρχει, εξαιρέστε τη διεύθυνσή μου: {%account_email_address%}.\nΕάν δεν μπορείτε να λάβετε μία ή περισσότερες από τις απαιτούμενες πληροφορίες, απαντήστε με μια κενή συμβολοσειρά.\nΔημιουργήστε μια απάντηση μόνο σε μορφή JSON. Μην συμπεριλάβετε κανένα επιπλέον κείμενο ή εξηγήσεις. Δώστε μόνο το JSON. Η μορφή που θα χρησιμοποιηθεί είναι η εξής:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"Σύνοψη συμβάντος ημερολογίου εδώ\",\n\"forceAllDay\": false,\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nΤο κείμενο είναι: \"{%mail_text_body_or_selected%}\"" }, "prompt_get_task": { "message": "Προσθήκη νέας εργασίας" From 7fb74d23e1a45016867aa96e7aec2d2442fdaf37 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 6 Feb 2026 18:59:31 +0100 Subject: [PATCH 29/51] Translated using Weblate (Croatian) Currently translated at 59.4% (274 of 461 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/hr/ --- _locales/hr/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_locales/hr/messages.json b/_locales/hr/messages.json index 4ab28369..570131bd 100644 --- a/_locales/hr/messages.json +++ b/_locales/hr/messages.json @@ -697,7 +697,7 @@ "message": "Dodaj novi kalendarski događaj" }, "prompt_get_calendar_event_full_text": { - "message": "Izdvoji sve relevantne detalje potrebne za generiranje kalendarskog događaja iz sljedećeg teksta. Izdvojene informacije trebaju uključivati:\n- Naslov događaja\n- Datum i vrijeme početka (uključujući vremensku zonu, ako je navedeno)\n- Datum i vrijeme završetka (uključujući vremensku zonu, ako je navedeno)\n- Cijeli dan (ako je navedeno)\n- Sudionici\nOsiguraj da su podaci oblikovani jasno i dosljedno kako bi se mogli izravno koristiti za stvaranje kalendarskog događaja.\nAko postoje relativne vremenske napomene, smatraj da su datum i vrijeme e-poruke \"{%mail_datetime%}\". Izračunajte datum i vrijeme početka na temelju ove napomene. Ako su izračunati početni datum i vrijeme raniji od \"{%current_datetime%}\", ponovno izračunaj početni datum i vrijeme koristeći \"{%current_datetime%}\" kao osnovu.\nAko trajanje nije navedeno, postavi ga na jedan sat.\nOvo su sudionici: {%author%}, {%recipients%}, {%cc_list%}. Ako je prisutna, isključi moju adresu: {%account_email_address%}.\nAko ne možeš dobiti jednu ili više potrebnih informacija, odgovori praznim nizom.\nGeneriraj odgovor samo u JSON formatu. Nemoj uključivati nikakav dodatni tekst ili objašnjenja; pruži samo JSON. Ovo je format koji će se koristiti:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"Sažetak kalendarskih događaja ovdje\",\n\"forceAllDay\": false\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nOvo je tekst:\"{%selected_text%}\"" + "message": "Izdvoji sve relevantne detalje potrebne za generiranje kalendarskog događaja iz sljedećeg teksta. Izdvojene informacije trebaju uključivati:\n- Naslov događaja\n- Datum i vrijeme početka (uključujući vremensku zonu, ako je navedeno)\n- Datum i vrijeme završetka (uključujući vremensku zonu, ako je navedeno)\n- Cijeli dan (ako je navedeno)\n- Sudionici\nOsiguraj da su podaci oblikovani jasno i dosljedno kako bi se mogli izravno koristiti za stvaranje kalendarskog događaja.\nAko postoje relativne vremenske napomene, smatraj da su datum i vrijeme e-poruke \"{%mail_datetime%}\". Izračunajte datum i vrijeme početka na temelju ove napomene. Ako su izračunati početni datum i vrijeme raniji od \"{%current_datetime%}\", ponovno izračunaj početni datum i vrijeme koristeći \"{%current_datetime%}\" kao osnovu.\nAko trajanje nije navedeno, postavi ga na jedan sat.\nOvo su sudionici: {%author%}, {%recipients%}, {%cc_list%}. Ako je prisutna, isključi moju adresu: {%account_email_address%}.\nAko ne možeš dobiti jednu ili više potrebnih informacija, odgovori praznim nizom.\nGeneriraj odgovor samo u JSON formatu. Nemoj uključivati nikakav dodatni tekst ili objašnjenja; pruži samo JSON. Ovo je format koji će se koristiti:\n{\n\"startDate\": \"YYYYMMDDTHHMMSS\",\n\"endDate\": \"YYYYMMDDTHHMMSS\",\n\"summary\": \"Sažetak kalendarskih događaja ovdje\",\n\"forceAllDay\": false\n\"attendees\": [attendee1@example.com,attendee2@example.com,attendee3@example.com]\n}\nOvo je tekst:\"{%mail_text_body_or_selected%}\"" }, "prefs_OptionText_get_calendar_event": { "message": "Dodaj novi kalendarski događaj iz odabranog teksta" From 6c490084419ec96bcdaf91a513f67d430320b674 Mon Sep 17 00:00:00 2001 From: mic Date: Fri, 6 Feb 2026 23:14:51 +0100 Subject: [PATCH 30/51] version set to 4.0.0pre1 --- manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifest.json b/manifest.json index 743688ab..9260970b 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "ThunderAI", "description": "__MSG_extensionDescription__", - "version": "4.0.0", + "version": "4.0.0pre1", "author": "Mic (m@micz.it)", "homepage_url": "https://micz.it/thunderbird-addon-thunderai/", "browser_specific_settings": { From ae010478ba9b9b0b6673beb0a1ca88c40e64cb5a Mon Sep 17 00:00:00 2001 From: mic Date: Fri, 6 Feb 2026 23:51:30 +0100 Subject: [PATCH 31/51] prompt copy button added. see #598 --- _locales/en/messages.json | 8 ++ pages/customprompts/mzta-custom-prompts.js | 94 ++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 87095fa4..6f9fa42a 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1852,5 +1852,13 @@ "prefs_OptionText_calendar_no_selection_Info": { "message": "If checked there is no need to select some text. The full message body will be used to get the calendar event.", "description": "" + }, + "customPrompts_btnCopy": { + "message": "Copy", + "description": "" + }, + "copy_text": { + "message": "copy", + "description": "" } } diff --git a/pages/customprompts/mzta-custom-prompts.js b/pages/customprompts/mzta-custom-prompts.js index 58bcbf04..55824f0d 100644 --- a/pages/customprompts/mzta-custom-prompts.js +++ b/pages/customprompts/mzta-custom-prompts.js @@ -324,6 +324,8 @@ document.addEventListener('DOMContentLoaded', async () => { let editBtn = document.querySelector(`tr[data-idnum="${curr_idnum}"] button.btnEditItem`); //console.log(`>>>>>>>>>>>> tr[data-idnum="${curr_idnum}"] button.btnEditItem`); editBtn.addEventListener('click', handleEditClick); + let copyBtn = document.querySelector(`tr[data-idnum="${curr_idnum}"] button.btnCopyItem`); + copyBtn.addEventListener('click', handleCopyClick); //console.log('>>>>>>>>>>>>> editBtn: ' + JSON.stringify(editBtn)); let deleteBtn = document.querySelector(`tr[data-idnum="${curr_idnum}"] button.btnDeleteItem`); //console.log(`>>>>>>>>>>>> tr[data-idnum="${curr_idnum}"] button.btnDeleteItem`); @@ -576,6 +578,7 @@ function handleEditClick(e) { tr.querySelector('.btnConfirmItem').style.display = 'inline'; // Save btn tr.querySelector('.btnCancelItem').style.display = 'inline'; // Cancel btn // tr.querySelector('.btnEditItem').style.display = 'none'; // Edit btn + tr.querySelector('.btnCopyItem').style.display = 'none'; // Copy btn tr.querySelector('.btnDeleteItem').style.display = 'none'; // Delete btn showItemRowEditor(tr); toggleDiffviewer(e); @@ -838,6 +841,7 @@ function handleCancelClick(e) { tr.querySelector('.btnConfirmItem').style.display = 'none'; // Save btn // tr.querySelector('.btnCancelItem').style.display = 'none'; // Cancel btn tr.querySelector('.btnEditItem').style.display = 'inline'; // Edit btn + tr.querySelector('.btnCopyItem').style.display = 'inline'; // Copy btn tr.querySelector('.btnDeleteItem').style.display = 'inline'; // Delete btn tr.querySelector('.id_output').value = tr.querySelector('.id_show').innerText.toLocaleUpperCase(); tr.querySelector('.name_output').value = tr.querySelector('.name_show').innerText; @@ -891,6 +895,7 @@ function handleConfirmClick(e) { // tr.querySelector('.btnConfirmItem').style.display = 'none'; // Ok btn tr.querySelector('.btnCancelItem').style.display = 'none'; // Cancel btn tr.querySelector('.btnEditItem').style.display = 'inline'; // Edit btn + tr.querySelector('.btnCopyItem').style.display = 'inline'; // Copy btn tr.querySelector('.btnDeleteItem').style.display = 'inline'; // Delete btn // Update item data tr.querySelector('.type').innerText = tr.querySelector('.type_output').value; @@ -942,6 +947,88 @@ function handleInputChange(e) { setSomethingChanged(); } +function handleCopyClick(e) { + e.preventDefault(); + const tr = e.target.parentNode.parentNode; + + let id = tr.querySelector('.id_output').value; + let name = tr.querySelector('.name_output').value; + let text = tr.querySelector('.text_output').value; + let type = tr.querySelector('.type_output').value; + let action = tr.querySelector('.action_output').value; + + let need_selected = tr.querySelector('.need_selected').checked; + let need_signature = tr.querySelector('.need_signature').checked; + let need_custom_text = tr.querySelector('.need_custom_text').checked; + let define_response_lang = tr.querySelector('.define_response_lang').checked; + let use_diff_viewer = tr.querySelector('.use_diff_viewer').checked; + + let chatgpt_web_model = tr.querySelector('.chatgpt_web_model_output').value; + let chatgpt_web_project = tr.querySelector('.chatgpt_web_project_output').value; + let chatgpt_web_custom_gpt = tr.querySelector('.chatgpt_web_custom_gpt_output').value; + + const item = promptsList.get('id', id.toLowerCase())[0]; + const itemValues = item.values(); + + // Populate new form + document.getElementById('txtIdNew').value = id + '_' + browser.i18n.getMessage("copy_text"); + document.getElementById('txtNameNew').value = name + ' (' + browser.i18n.getMessage("copy_text") + ')'; + document.getElementById('txtTextNew').value = text; + document.getElementById('selectTypeNew').value = type; + document.getElementById('selectActionNew').value = action; + + document.getElementById('checkboxNeedSelectedNew').checked = need_selected; + document.getElementById('checkboxNeedSignatureNew').checked = need_signature; + document.getElementById('checkboxNeedCustomTextNew').checked = need_custom_text; + document.getElementById('checkboxDefineResponseLangNew').checked = define_response_lang; + + let checkboxUseDiffViewerNew = document.getElementById('checkboxUseDiffViewerNew'); + checkboxUseDiffViewerNew.checked = use_diff_viewer; + checkboxUseDiffViewerNew.disabled = (action !== "2"); + + document.getElementById('chatGPTWebModelNew').value = chatgpt_web_model; + document.getElementById('chatGPTWebProjectNew').value = chatgpt_web_project; + document.getElementById('chatGPTWebCustomGPTNew').value = chatgpt_web_custom_gpt; + + const apiSelect = document.getElementById('new_prompt_api_type'); + if (apiSelect) { + apiSelect.value = itemValues.api_type || ''; + apiSelect.dispatchEvent(new Event('change')); + + for (const [integration, options] of Object.entries(integration_options_config)) { + for (const key of Object.keys(options)) { + const propName = `${integration}_${key}`; + const inputEl = document.getElementById(propName); + if (inputEl) { + let val = itemValues[propName]; + if (val === undefined) val = ''; + + if (inputEl.type === 'checkbox') { + inputEl.checked = (val === true || val === 'true'); + } else { + if (inputEl.tomselect) { + inputEl.tomselect.setValue(val, true); + setTomSelectBorder(inputEl.tomselect); + } else { + inputEl.value = val; + } + } + } + } + } + } + + // Show form + document.getElementById('formNew').style.display = 'block'; + + // Scroll to top + window.scrollTo({ + top: 0, + behavior: 'smooth' + }); + checkFields(); +} + //========= handling an item in a row - END @@ -1071,6 +1158,8 @@ function loadPromptsList(values){

    +

    + `; //console.log('>>>>>>>> values.name: ' + JSON.stringify(values.name)); @@ -1111,6 +1200,11 @@ function loadPromptsList(values){ element.addEventListener('click', handleEditClick); }); + let btnCopyItem_elements = document.querySelectorAll(".btnCopyItem"); + btnCopyItem_elements.forEach(element => { + element.addEventListener('click', handleCopyClick); + }); + let btnDeleteItem_elements = document.querySelectorAll(".btnDeleteItem"); btnDeleteItem_elements.forEach(element => { element.addEventListener('click', handleDeleteClick); From 10a26210d08eec1ea39e2a49039af68e73104d73 Mon Sep 17 00:00:00 2001 From: mic Date: Fri, 6 Feb 2026 23:52:45 +0100 Subject: [PATCH 32/51] release notes updated --- CHANGELOG.md | 1 + options/mzta-release-notes.html | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10589696..bc9be316 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@
  • Added the {%mail_html_body_or_selected%} placeholder to retrieve the selected HTML or the full HTML body of the email if no selection is present [#641].
  • [All APIs] Added an option to get a calendar event without selecting some text, but using the full text body of the email [#518].
  • [All APIs] Added a new menu item to create a calendar event from the text saved in the clipboard [#362].
  • +
  • Added a button to copy a prompt in the Custom Prompts page [#598].
  • ...
  • Version 3.8.3 - 22/01/2026

    diff --git a/options/mzta-release-notes.html b/options/mzta-release-notes.html index 752d1aad..d1c22e70 100644 --- a/options/mzta-release-notes.html +++ b/options/mzta-release-notes.html @@ -22,6 +22,7 @@
  • Added the {%mail_html_body_or_selected%} placeholder to retrieve the selected HTML or the full HTML body of the email if no selection is present [#641].
  • [All APIs] Added an option to get a calendar event without selecting some text, but using the full text body of the email [#518].
  • [All APIs] Added a new menu item to create a calendar event from the text saved in the clipboard [#362].
  • +
  • Added a button to copy a prompt in the Custom Prompts page [#598].
  • ...
  • Version 3.8.3 - 22/01/2026

    From a940228a43bbfc8c2721d6cc022fba8854bf9937 Mon Sep 17 00:00:00 2001 From: mic Date: Sun, 8 Feb 2026 00:10:39 +0100 Subject: [PATCH 33/51] showing spam value. see #506 --- _locales/en/messages.json | 8 ++++++++ js/mzta-compose-script.js | 41 ++++++++++++++++++++++++++++++++++++++- mzta-background.js | 16 +++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 6f9fa42a..304a4e50 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1379,6 +1379,14 @@ "message": "Spam Value", "description": "" }, + "Spam": { + "message": "Spam", + "description": "" + }, + "Valid": { + "message": "Valid", + "description": "" + }, "Moved_to_Spam": { "message": "Moved to Spam", "description": "" diff --git a/js/mzta-compose-script.js b/js/mzta-compose-script.js index 972a32de..3d74322e 100644 --- a/js/mzta-compose-script.js +++ b/js/mzta-compose-script.js @@ -617,9 +617,48 @@ switch (message.command) { break; + case "showSpamReport": + const data = message.data; + if(document.getElementById('mzta-spam-report-banner')) return Promise.resolve(true); + + const container = document.createElement('div'); + container.id = 'mzta-spam-report-banner'; + + const isDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; + + let bgColor = '#f8f9fa'; + let textColor = '#333'; + let borderColor = '#ccc'; + + if (data.spamValue < (data.SpamThreshold || 50)) { + bgColor = isDark ? '#5a1a1a' : '#ffe6e6'; + textColor = isDark ? '#ffcccc' : '#cc0000'; + borderColor = '#cc0000'; + } else { + bgColor = isDark ? '#1a401a' : '#e6ffe6'; + textColor = isDark ? '#ccffcc' : '#006600'; + borderColor = '#006600'; + } + + container.style.cssText = `background-color: ${bgColor}; color: ${textColor}; border-bottom: 1px solid ${borderColor}; padding: 8px 12px; font-family: system-ui, -apple-system, sans-serif; font-size: 13px; display: flex; align-items: center; gap: 15px; width: 100%; box-sizing: border-box;`; + + const scoreText = document.createElement('strong'); + scoreText.textContent = ((data.spamValue < (data.SpamThreshold || 50)) ? browser.i18n.getMessage("Spam") : browser.i18n.getMessage("Valid")) + " [" + data.spamValue + "/100]"; + + const reasonText = document.createElement('span'); + reasonText.textContent = browser.i18n.getMessage("Explanation") + ": " + data.explanation; + + container.appendChild(scoreText); + container.appendChild(reasonText); + + document.body.insertBefore(container, document.body.firstChild); + return Promise.resolve(true); + default: // do nothing return Promise.resolve(false); break; } -}); \ No newline at end of file +}); + +browser.runtime.sendMessage({ command: "checkSpamReport" }); \ No newline at end of file diff --git a/mzta-background.js b/mzta-background.js index 5fee2dd6..886f7384 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -358,6 +358,22 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => { case 'api_send_custom_text': browser.tabs.sendMessage(message.tabId, { command: "api_send_custom_text", custom_text: message.custom_text }); break; + case 'checkSpamReport': + async function _checkSpamReport(tabId) { + try { + if (sender.tab.type !== 'messageDisplay' && sender.tab.type !== 'mail') return; + let message = await browser.messageDisplay.getDisplayedMessage(tabId); + if (!message) return; + let report = await taSpamReport.loadReportData(message.headerMessageId); + if (report) { + browser.tabs.sendMessage(tabId, { command: "showSpamReport", data: report }); + } + } catch (e) { + taLog.error("Error in checkSpamReport: " + e); + } + } + _checkSpamReport(sender.tab.id); + break; default: break; } From 500572fee15d04bb1fcfdb8c7e87e1063033d8b2 Mon Sep 17 00:00:00 2001 From: Andreas Pettersson Date: Sat, 7 Feb 2026 22:37:52 +0100 Subject: [PATCH 34/51] Translated using Weblate (Swedish) Currently translated at 100.0% (463 of 463 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/sv/ --- _locales/sv/messages.json | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/_locales/sv/messages.json b/_locales/sv/messages.json index d3468983..b6062c13 100644 --- a/_locales/sv/messages.json +++ b/_locales/sv/messages.json @@ -1358,5 +1358,44 @@ }, "customPrompts_export_include_api_settings": { "message": "Vill du inkludera API-inställningar i exporten? Tänk på att även API-nyckeln kommer att sparas i filen!" + }, + "prompt_get_calendar_event_from_clipboard": { + "message": "Lägg till kalenderhändelse från urklipp" + }, + "clipboard_read_error": { + "message": "Kunde inte läsa urklipp. Kontrollera behörigheterna." + }, + "clipboard_empty_error": { + "message": "Urklipp är tom. Kopiera lite text först." + }, + "clipboard_permission_denied": { + "message": "Åtkomst till urklipp nekades. Aktivera funktionen igen i inställningarna för att ge behörighet." + }, + "clipboard_permission_error": { + "message": "Fel vid begäran om behörighet för urklipp. Försök igen." + }, + "prefs_OptionText_get_calendar_event_from_clipboard": { + "message": "Hämta kalenderhändelse från urklipp" + }, + "prefs_OptionText_get_calendar_event_from_clipboard_Info": { + "message": "Visa ytterligare ett menyalternativ för att skapa kalenderhändelser från textinnehåll i urklipp." + }, + "prefs_OptionText_calendar_no_selection": { + "message": "Fråga inte om att markera text" + }, + "prefs_OptionText_calendar_no_selection_Info": { + "message": "Om markerad behöver du inte markera någon text. Hela meddelandets innehåll kommer att användas för att hämta kalenderhändelsen." + }, + "customPrompts_btnCopy": { + "message": "Kopiera" + }, + "copy_text": { + "message": "kopiera" + }, + "placeholder_mail_text_body_or_selected": { + "message": "Mejltext eller markerad text" + }, + "placeholder_mail_html_body_or_selected": { + "message": "Mejltext eller vald HTML" } } From ef9a6a0e07bbf82ca270b0dba3ded24f49c4ebbf Mon Sep 17 00:00:00 2001 From: Andreas Pettersson Date: Sun, 8 Feb 2026 14:30:14 +0100 Subject: [PATCH 35/51] Translated using Weblate (Swedish) Currently translated at 100.0% (465 of 465 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/sv/ --- _locales/sv/messages.json | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/_locales/sv/messages.json b/_locales/sv/messages.json index b6062c13..5ac1a1b6 100644 --- a/_locales/sv/messages.json +++ b/_locales/sv/messages.json @@ -1397,5 +1397,11 @@ }, "placeholder_mail_html_body_or_selected": { "message": "Mejltext eller vald HTML" + }, + "Spam": { + "message": "Skräppost" + }, + "Valid": { + "message": "Giltig" } } From bf29673600e453d20120f1349610be354ad124a1 Mon Sep 17 00:00:00 2001 From: mic Date: Sun, 8 Feb 2026 21:14:07 +0100 Subject: [PATCH 36/51] tom select udpated to 2.4.6. fixes #644 --- VENDORS.md | 2 +- pages/_lib/tom-select.base.js | 34 +++++++++++++++++---------- pages/_lib/tom-select.default.min.css | 2 +- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/VENDORS.md b/VENDORS.md index c47d117d..f0c79411 100644 --- a/VENDORS.md +++ b/VENDORS.md @@ -2,7 +2,7 @@ file: pages\_lib\list.js source: https://raw.githubusercontent.com/javve/list.js/v2.3.1/dist/list.js file: pages\_lib\tom-select.base.js -source: https://cdn.jsdelivr.net/npm/tom-select@2.4.4/dist/js/tom-select.base.js +source: https://cdn.jsdelivr.net/npm/tom-select@2.4.6/dist/js/tom-select.base.js file: js\lib\diff.js source: https://cdnjs.cloudflare.com/ajax/libs/jsdiff/7.0.0/diff.js \ No newline at end of file diff --git a/pages/_lib/tom-select.base.js b/pages/_lib/tom-select.base.js index 3a36e988..a79fe394 100644 --- a/pages/_lib/tom-select.base.js +++ b/pages/_lib/tom-select.base.js @@ -1,5 +1,5 @@ /** -* Tom Select v2.4.4 +* Tom Select v2.4.6 * Licensed under the Apache License, Version 2.0 (the "License"); */ @@ -1889,6 +1889,8 @@ }); } wrapper.style.width = input.style.width; + wrapper.style.minWidth = input.style.minWidth; + wrapper.style.maxWidth = input.style.maxWidth; if (self.plugins.names.length) { const classes_plugins = 'plugin-' + self.plugins.names.join(' plugin-'); addClasses([wrapper, dropdown], classes_plugins); @@ -1985,10 +1987,19 @@ self.positionDropdown(); } }; + const input_invalid = () => { + if (self.isValid) { + self.isValid = false; + self.isInvalid = true; + self.refreshState(); + } + }; + addEvent(input, 'invalid', input_invalid); addEvent(document, 'mousedown', doc_mousedown); addEvent(window, 'scroll', win_scroll, passive_event); addEvent(window, 'resize', win_scroll, passive_event); this._destroy = () => { + input.removeEventListener('invalid', input_invalid); document.removeEventListener('mousedown', doc_mousedown); window.removeEventListener('scroll', win_scroll); window.removeEventListener('resize', win_scroll); @@ -2007,13 +2018,6 @@ settings.items = []; delete settings.optgroups; delete settings.options; - addEvent(input, 'invalid', () => { - if (self.isValid) { - self.isValid = false; - self.isInvalid = true; - self.refreshState(); - } - }); self.refreshItems(); self.close(false); self.inputState(); @@ -2128,7 +2132,8 @@ sync(get_settings = true) { const self = this; const settings = get_settings ? getSettings(self.input, { - delimiter: self.settings.delimiter + delimiter: self.settings.delimiter, + allowEmptyOption: self.settings.allowEmptyOption }) : self.settings; self.setupOptions(settings.options, settings.optgroups); self.setValue(settings.items || [], true); // silent prevents recursion @@ -2315,8 +2320,7 @@ // prevent default [tab] behaviour of jump to the next field // if select isFull, then the dropdown won't be open and [tab] will work normally preventDefault(e); - } - if (self.settings.create && self.createItem()) { + } else if (self.settings.create && self.createItem()) { preventDefault(e); } } @@ -3512,6 +3516,12 @@ var output; input = input || self.inputValue(); if (!self.canCreate(input)) { + const hash = hash_key(input); + if (hash) { + if (this.options[input]) { + self.addItem(input); + } + } callback(); return false; } @@ -3831,7 +3841,7 @@ const values = items.map(item => item.dataset.value); // allow the callback to abort - if (!values.length || typeof this.settings.onDelete === 'function' && this.settings.onDelete(values, evt) === false) { + if (!values.length || typeof this.settings.onDelete === 'function' && this.settings.onDelete.call(this, values, evt) === false) { return false; } return true; diff --git a/pages/_lib/tom-select.default.min.css b/pages/_lib/tom-select.default.min.css index db7cd29f..c98a1b3d 100644 --- a/pages/_lib/tom-select.default.min.css +++ b/pages/_lib/tom-select.default.min.css @@ -1,2 +1,2 @@ -.ts-control{border:1px solid #d0d0d0;border-radius:3px;box-shadow:inset 0 1px 1px rgba(0,0,0,.1);box-sizing:border-box;display:flex;flex-wrap:wrap;overflow:hidden;padding:8px;position:relative;width:100%;z-index:1}.ts-wrapper.multi.has-items .ts-control{padding:5px 8px 2px}.full .ts-control{background-color:#fff}.disabled .ts-control,.disabled .ts-control *{cursor:default!important}.focus .ts-control{box-shadow:inset 0 1px 2px rgba(0,0,0,.15)}.ts-control>*{display:inline-block;vertical-align:initial}.ts-wrapper.multi .ts-control>div{background:#1da7ee;border:1px solid #0073bb;color:#fff;cursor:pointer;margin:0 3px 3px 0;padding:2px 6px}.ts-wrapper.multi .ts-control>div.active{background:#92c836;border:1px solid #00578d;color:#fff}.ts-wrapper.multi.disabled .ts-control>div,.ts-wrapper.multi.disabled .ts-control>div.active{background:#d2d2d2;border:1px solid #aaa;color:#fff}.ts-control>input{background:none!important;border:0!important;box-shadow:none!important;display:inline-block!important;flex:1 1 auto;line-height:inherit!important;margin:0!important;max-height:none!important;max-width:100%!important;min-height:0!important;min-width:7rem;padding:0!important;text-indent:0!important;-webkit-user-select:auto!important;-moz-user-select:auto!important;-ms-user-select:auto!important;user-select:auto!important}.ts-control>input::-ms-clear{display:none}.ts-control>input:focus{outline:none!important}.has-items .ts-control>input{margin:0 4px!important}.ts-control.rtl{text-align:right}.ts-control.rtl.single .ts-control:after{left:15px;right:auto}.ts-control.rtl .ts-control>input{margin:0 4px 0 -2px!important}.disabled .ts-control{background-color:#fafafa;opacity:.5}.input-hidden .ts-control>input{left:-10000px;opacity:0;position:absolute}.ts-dropdown{background:#fff;border:1px solid #d0d0d0;border-radius:0 0 3px 3px;border-top:0;box-shadow:0 1px 3px rgba(0,0,0,.1);box-sizing:border-box;left:0;margin:.25rem 0 0;position:absolute;top:100%;width:100%;z-index:10}.ts-dropdown [data-selectable]{cursor:pointer;overflow:hidden}.ts-dropdown [data-selectable] .highlight{background:rgba(125,168,208,.2);border-radius:1px}.ts-dropdown .create,.ts-dropdown .no-results,.ts-dropdown .optgroup-header,.ts-dropdown .option{padding:5px 8px}.ts-dropdown .option,.ts-dropdown [data-disabled],.ts-dropdown [data-disabled] [data-selectable].option{cursor:inherit;opacity:.5}.ts-dropdown [data-selectable].option{cursor:pointer;opacity:1}.ts-dropdown .optgroup:first-child .optgroup-header{border-top:0}.ts-dropdown .optgroup-header{background:#fff;color:#303030;cursor:default}.ts-dropdown .active{background-color:#f5fafd;color:#495c68}.ts-dropdown .active.create{color:#495c68}.ts-dropdown .create{color:rgba(48,48,48,.5)}.ts-dropdown .spinner{display:inline-block;height:30px;margin:5px 8px;width:30px}.ts-dropdown .spinner:after{animation:lds-dual-ring 1.2s linear infinite;border-color:#d0d0d0 transparent;border-radius:50%;border-style:solid;border-width:5px;content:" ";display:block;height:24px;margin:3px;width:24px}@keyframes lds-dual-ring{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.ts-dropdown-content{max-height:200px;overflow:hidden auto;scroll-behavior:smooth}.ts-wrapper.plugin-drag_drop .ts-dragging{color:transparent!important}.ts-wrapper.plugin-drag_drop .ts-dragging>*{visibility:hidden!important}.plugin-checkbox_options:not(.rtl) .option input{margin-right:.5rem}.plugin-checkbox_options.rtl .option input{margin-left:.5rem}.plugin-clear_button{--ts-pr-clear-button:1em}.plugin-clear_button .clear-button{background:transparent!important;cursor:pointer;margin-right:0!important;opacity:0;position:absolute;right:2px;top:50%;transform:translateY(-50%);transition:opacity .5s}.plugin-clear_button.form-select .clear-button,.plugin-clear_button.single .clear-button{right:max(var(--ts-pr-caret),8px)}.plugin-clear_button.focus.has-items .clear-button,.plugin-clear_button:not(.disabled):hover.has-items .clear-button{opacity:1}.ts-wrapper .dropdown-header{background:color-mix(#fff,#d0d0d0,85%);border-bottom:1px solid #d0d0d0;border-radius:3px 3px 0 0;padding:10px 8px;position:relative}.ts-wrapper .dropdown-header-close{color:#303030;font-size:20px!important;line-height:20px;margin-top:-12px;opacity:.4;position:absolute;right:8px;top:50%}.ts-wrapper .dropdown-header-close:hover{color:#000}.plugin-dropdown_input.focus.dropdown-active .ts-control{border:1px solid #d0d0d0;box-shadow:none}.plugin-dropdown_input .dropdown-input{background:transparent;border:solid #d0d0d0;border-width:0 0 1px;box-shadow:inset 0 1px 1px rgba(0,0,0,.1);display:block;padding:8px;width:100%}.plugin-dropdown_input .items-placeholder{border:0!important;box-shadow:none!important;width:100%}.plugin-dropdown_input.dropdown-active .items-placeholder,.plugin-dropdown_input.has-items .items-placeholder{display:none!important}.ts-wrapper.plugin-input_autogrow.has-items .ts-control>input{min-width:0}.ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control>input{flex:none;min-width:4px}.ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control>input::-ms-input-placeholder{color:transparent}.ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control>input::placeholder{color:transparent}.ts-dropdown.plugin-optgroup_columns .ts-dropdown-content{display:flex}.ts-dropdown.plugin-optgroup_columns .optgroup{border-right:1px solid #f2f2f2;border-top:0;flex-basis:0;flex-grow:1;min-width:0}.ts-dropdown.plugin-optgroup_columns .optgroup:last-child{border-right:0}.ts-dropdown.plugin-optgroup_columns .optgroup:before{display:none}.ts-dropdown.plugin-optgroup_columns .optgroup-header{border-top:0}.ts-wrapper.plugin-remove_button .item{align-items:center;display:inline-flex}.ts-wrapper.plugin-remove_button .item .remove{border-radius:0 2px 2px 0;box-sizing:border-box;color:inherit;display:inline-block;padding:0 6px;text-decoration:none;vertical-align:middle}.ts-wrapper.plugin-remove_button .item .remove:hover{background:rgba(0,0,0,.05)}.ts-wrapper.plugin-remove_button.disabled .item .remove:hover{background:none}.ts-wrapper.plugin-remove_button .remove-single{font-size:23px;position:absolute;right:0;top:0}.ts-wrapper.plugin-remove_button:not(.rtl) .item{padding-right:0!important}.ts-wrapper.plugin-remove_button:not(.rtl) .item .remove{border-left:1px solid #0073bb;margin-left:6px}.ts-wrapper.plugin-remove_button:not(.rtl) .item.active .remove{border-left-color:#00578d}.ts-wrapper.plugin-remove_button:not(.rtl).disabled .item .remove{border-left-color:#aaa}.ts-wrapper.plugin-remove_button.rtl .item{padding-left:0!important}.ts-wrapper.plugin-remove_button.rtl .item .remove{border-right:1px solid #0073bb;margin-right:6px}.ts-wrapper.plugin-remove_button.rtl .item.active .remove{border-right-color:#00578d}.ts-wrapper.plugin-remove_button.rtl.disabled .item .remove{border-right-color:#aaa}:root{--ts-pr-clear-button:0px;--ts-pr-caret:0px;--ts-pr-min:.75rem}.ts-wrapper.single .ts-control,.ts-wrapper.single .ts-control input{cursor:pointer}.ts-control:not(.rtl){padding-right:max(var(--ts-pr-min),var(--ts-pr-clear-button) + var(--ts-pr-caret))!important}.ts-control.rtl{padding-left:max(var(--ts-pr-min),var(--ts-pr-clear-button) + var(--ts-pr-caret))!important}.ts-wrapper{position:relative}.ts-control,.ts-control input,.ts-dropdown{color:#303030;font-family:inherit;font-size:13px;line-height:18px}.ts-control,.ts-wrapper.single.input-active .ts-control{background:#fff;cursor:text}.ts-hidden-accessible{border:0!important;clip:rect(0 0 0 0)!important;-webkit-clip-path:inset(50%)!important;clip-path:inset(50%)!important;overflow:hidden!important;padding:0!important;position:absolute!important;white-space:nowrap!important;width:1px!important}.ts-wrapper.single .ts-control{--ts-pr-caret:2rem}.ts-wrapper.single .ts-control:after{border-color:grey transparent transparent;border-style:solid;border-width:5px 5px 0;content:" ";display:block;height:0;margin-top:-3px;position:absolute;top:50%;width:0}.ts-wrapper.single .ts-control:not(.rtl):after{right:15px}.ts-wrapper.single .ts-control.rtl:after{left:15px}.ts-wrapper.single.dropdown-active .ts-control:after{border-color:transparent transparent grey;border-width:0 5px 5px;margin-top:-4px}.ts-wrapper.single.input-active .ts-control,.ts-wrapper.single.input-active .ts-control input{cursor:text}.ts-wrapper{display:flex;min-height:36px}.ts-wrapper.multi.has-items .ts-control{padding-left:5px;--ts-pr-min:$padding-x}.ts-wrapper.multi .ts-control [data-value]{background-color:color-mix(#1da7ee,#178ee9,60%);background-image:linear-gradient(180deg,#1da7ee,#178ee9);background-repeat:repeat-x;border-radius:3px;box-shadow:0 1px 0 rgba(0,0,0,.2),inset 0 1px hsla(0,0%,100%,.03);text-shadow:0 1px 0 rgba(0,51,83,.3)}.ts-wrapper.multi .ts-control [data-value].active{background-color:color-mix(#008fd8,#0075cf,60%);background-image:linear-gradient(180deg,#008fd8,#0075cf);background-repeat:repeat-x}.ts-wrapper.multi.disabled .ts-control [data-value]{background:none;box-shadow:none;color:#999;text-shadow:none}.ts-wrapper.multi.disabled .ts-control [data-value],.ts-wrapper.multi.disabled .ts-control [data-value] .remove{border-color:#e6e6e6}.ts-wrapper.multi.disabled .ts-control [data-value] .remove{background:none}.ts-wrapper.single .ts-control{background-color:color-mix(#fefefe,#f2f2f2,60%);background-image:linear-gradient(180deg,#fefefe,#f2f2f2);background-repeat:repeat-x;box-shadow:0 1px 0 rgba(0,0,0,.05),inset 0 1px 0 hsla(0,0%,100%,.8)}.ts-dropdown.single,.ts-wrapper.single .ts-control{border-color:#b8b8b8}.dropdown-active .ts-control{border-radius:3px 3px 0 0}.ts-dropdown .optgroup-header{font-size:.85em;font-weight:700;padding-top:7px}.ts-dropdown .optgroup{border-top:1px solid #f0f0f0}.ts-dropdown .optgroup:first-child{border-top:0} +.ts-control{border:1px solid #d0d0d0;border-radius:3px;box-shadow:inset 0 1px 1px rgba(0,0,0,.1);box-sizing:border-box;display:flex;flex-wrap:wrap;overflow:hidden;padding:8px;position:relative;width:100%;z-index:1}.ts-wrapper.multi.has-items .ts-control{padding:5px 8px 2px}.full .ts-control{background-color:#fff}.disabled .ts-control,.disabled .ts-control *{cursor:default!important}.focus .ts-control{box-shadow:inset 0 1px 2px rgba(0,0,0,.15)}.ts-control>*{display:inline-block;vertical-align:initial}.ts-wrapper.multi .ts-control>div{background:#1da7ee;border:1px solid #0073bb;color:#fff;cursor:pointer;margin:0 3px 3px 0;overflow:auto;padding:2px 6px}.ts-wrapper.multi .ts-control>div.active{background:#92c836;border:1px solid #00578d;color:#fff}.ts-wrapper.multi.disabled .ts-control>div,.ts-wrapper.multi.disabled .ts-control>div.active{background:#d2d2d2;border:1px solid #aaa;color:#fff}.ts-control>input{background:none!important;border:0!important;box-shadow:none!important;display:inline-block!important;flex:1 1 auto;line-height:inherit!important;margin:0!important;max-height:none!important;max-width:100%!important;min-height:0!important;min-width:7rem;padding:0!important;text-indent:0!important;-webkit-user-select:auto!important;-moz-user-select:auto!important;-ms-user-select:auto!important;user-select:auto!important}.ts-control>input::-ms-clear{display:none}.ts-control>input:focus{outline:none!important}.has-items .ts-control>input{margin:0 4px!important}.ts-control.rtl{text-align:right}.ts-control.rtl.single .ts-control:after{left:15px;right:auto}.ts-control.rtl .ts-control>input{margin:0 4px 0 -2px!important}.disabled .ts-control{background-color:#fafafa;opacity:.5}.input-hidden .ts-control>input{left:-10000px;opacity:0;position:absolute}.ts-dropdown{background:#fff;border:1px solid #d0d0d0;border-radius:0 0 3px 3px;border-top:0;box-shadow:0 1px 3px rgba(0,0,0,.1);box-sizing:border-box;left:0;margin:.25rem 0 0;position:absolute;top:100%;width:100%;z-index:10}.ts-dropdown [data-selectable]{cursor:pointer;overflow:hidden}.ts-dropdown [data-selectable] .highlight{background:rgba(125,168,208,.2);border-radius:1px}.ts-dropdown .create,.ts-dropdown .no-results,.ts-dropdown .optgroup-header,.ts-dropdown .option{padding:5px 8px}.ts-dropdown .option,.ts-dropdown [data-disabled],.ts-dropdown [data-disabled] [data-selectable].option{cursor:inherit;opacity:.5}.ts-dropdown [data-selectable].option{cursor:pointer;opacity:1}.ts-dropdown .optgroup:first-child .optgroup-header{border-top:0}.ts-dropdown .optgroup-header{background:#fff;color:#303030;cursor:default}.ts-dropdown .active{background-color:#f5fafd;color:#495c68}.ts-dropdown .active.create{color:#495c68}.ts-dropdown .create{color:rgba(48,48,48,.5)}.ts-dropdown .spinner{display:inline-block;height:30px;margin:5px 8px;width:30px}.ts-dropdown .spinner:after{animation:lds-dual-ring 1.2s linear infinite;border-color:#d0d0d0 transparent;border-radius:50%;border-style:solid;border-width:5px;content:" ";display:block;height:24px;margin:3px;width:24px}@keyframes lds-dual-ring{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.ts-dropdown-content{max-height:200px;overflow:hidden auto;scroll-behavior:smooth}.ts-wrapper.plugin-drag_drop .ts-dragging{color:transparent!important}.ts-wrapper.plugin-drag_drop .ts-dragging>*{visibility:hidden!important}.plugin-checkbox_options:not(.rtl) .option input{margin-right:.5rem}.plugin-checkbox_options.rtl .option input{margin-left:.5rem}.plugin-clear_button{--ts-pr-clear-button:1em}.plugin-clear_button .clear-button{background:transparent!important;cursor:pointer;margin-right:0!important;opacity:0;position:absolute;right:2px;top:50%;transform:translateY(-50%);transition:opacity .5s}.plugin-clear_button.form-select .clear-button,.plugin-clear_button.single .clear-button{right:max(var(--ts-pr-caret),8px)}.plugin-clear_button.focus.has-items .clear-button,.plugin-clear_button:not(.disabled):hover.has-items .clear-button{opacity:1}.ts-wrapper .dropdown-header{background:color-mix(#fff,#d0d0d0,85%);border-bottom:1px solid #d0d0d0;border-radius:3px 3px 0 0;padding:10px 8px;position:relative}.ts-wrapper .dropdown-header-close{color:#303030;font-size:20px!important;line-height:20px;margin-top:-12px;opacity:.4;position:absolute;right:8px;top:50%}.ts-wrapper .dropdown-header-close:hover{color:#000}.plugin-dropdown_input.focus.dropdown-active .ts-control{border:1px solid #d0d0d0;box-shadow:none}.plugin-dropdown_input .dropdown-input{background:transparent;border:solid #d0d0d0;border-width:0 0 1px;box-shadow:inset 0 1px 1px rgba(0,0,0,.1);display:block;padding:8px;width:100%}.plugin-dropdown_input .items-placeholder{border:0!important;box-shadow:none!important;width:100%}.plugin-dropdown_input.dropdown-active .items-placeholder,.plugin-dropdown_input.has-items .items-placeholder{display:none!important}.ts-wrapper.plugin-input_autogrow.has-items .ts-control>input{min-width:0}.ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control>input{flex:none;min-width:4px}.ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control>input::-ms-input-placeholder{color:transparent}.ts-wrapper.plugin-input_autogrow.has-items.focus .ts-control>input::placeholder{color:transparent}.ts-dropdown.plugin-optgroup_columns .ts-dropdown-content{display:flex}.ts-dropdown.plugin-optgroup_columns .optgroup{border-right:1px solid #f2f2f2;border-top:0;flex-basis:0;flex-grow:1;min-width:0}.ts-dropdown.plugin-optgroup_columns .optgroup:last-child{border-right:0}.ts-dropdown.plugin-optgroup_columns .optgroup:before{display:none}.ts-dropdown.plugin-optgroup_columns .optgroup-header{border-top:0}.ts-wrapper.plugin-remove_button .item{align-items:center;display:inline-flex}.ts-wrapper.plugin-remove_button .item .remove{border-radius:0 2px 2px 0;box-sizing:border-box;color:inherit;display:inline-block;padding:0 6px;text-decoration:none;vertical-align:middle}.ts-wrapper.plugin-remove_button .item .remove:hover{background:rgba(0,0,0,.05)}.ts-wrapper.plugin-remove_button.disabled .item .remove:hover{background:none}.ts-wrapper.plugin-remove_button .remove-single{font-size:23px;position:absolute;right:0;top:0}.ts-wrapper.plugin-remove_button:not(.rtl) .item{padding-right:0!important}.ts-wrapper.plugin-remove_button:not(.rtl) .item .remove{border-left:1px solid #0073bb;margin-left:6px}.ts-wrapper.plugin-remove_button:not(.rtl) .item.active .remove{border-left-color:#00578d}.ts-wrapper.plugin-remove_button:not(.rtl).disabled .item .remove{border-left-color:#aaa}.ts-wrapper.plugin-remove_button.rtl .item{padding-left:0!important}.ts-wrapper.plugin-remove_button.rtl .item .remove{border-right:1px solid #0073bb;margin-right:6px}.ts-wrapper.plugin-remove_button.rtl .item.active .remove{border-right-color:#00578d}.ts-wrapper.plugin-remove_button.rtl.disabled .item .remove{border-right-color:#aaa}:root{--ts-pr-clear-button:0px;--ts-pr-caret:0px;--ts-pr-min:.75rem}.ts-wrapper.single .ts-control,.ts-wrapper.single .ts-control input{cursor:pointer}.ts-control:not(.rtl){padding-right:max(var(--ts-pr-min),var(--ts-pr-clear-button) + var(--ts-pr-caret))!important}.ts-control.rtl{padding-left:max(var(--ts-pr-min),var(--ts-pr-clear-button) + var(--ts-pr-caret))!important}.ts-wrapper{position:relative}.ts-control,.ts-control input,.ts-dropdown{color:#303030;font-family:inherit;font-size:13px;line-height:18px}.ts-control,.ts-wrapper.single.input-active .ts-control{background:#fff;cursor:text}.ts-hidden-accessible{border:0!important;clip:rect(0 0 0 0)!important;-webkit-clip-path:inset(50%)!important;clip-path:inset(50%)!important;overflow:hidden!important;padding:0!important;position:absolute!important;white-space:nowrap!important;width:1px!important}.ts-wrapper.single .ts-control{--ts-pr-caret:2rem}.ts-wrapper.single .ts-control:after{border-color:grey transparent transparent;border-style:solid;border-width:5px 5px 0;content:" ";display:block;height:0;margin-top:-3px;position:absolute;top:50%;width:0}.ts-wrapper.single .ts-control:not(.rtl):after{right:15px}.ts-wrapper.single .ts-control.rtl:after{left:15px}.ts-wrapper.single.dropdown-active .ts-control:after{border-color:transparent transparent grey;border-width:0 5px 5px;margin-top:-4px}.ts-wrapper.single.input-active .ts-control,.ts-wrapper.single.input-active .ts-control input{cursor:text}.ts-wrapper{display:flex;min-height:36px}.ts-wrapper.multi.has-items .ts-control{padding-left:5px;--ts-pr-min:5px}.ts-wrapper.multi .ts-control [data-value]{background-color:color-mix(#1da7ee,#178ee9,60%);background-image:linear-gradient(180deg,#1da7ee,#178ee9);background-repeat:repeat-x;border-radius:3px;box-shadow:0 1px 0 rgba(0,0,0,.2),inset 0 1px hsla(0,0%,100%,.03);text-shadow:0 1px 0 rgba(0,51,83,.3)}.ts-wrapper.multi .ts-control [data-value].active{background-color:color-mix(#008fd8,#0075cf,60%);background-image:linear-gradient(180deg,#008fd8,#0075cf);background-repeat:repeat-x}.ts-wrapper.multi.disabled .ts-control [data-value]{background:none;box-shadow:none;color:#999;text-shadow:none}.ts-wrapper.multi.disabled .ts-control [data-value],.ts-wrapper.multi.disabled .ts-control [data-value] .remove{border-color:#e6e6e6}.ts-wrapper.multi.disabled .ts-control [data-value] .remove{background:none}.ts-wrapper.single .ts-control{background-color:color-mix(#fefefe,#f2f2f2,60%);background-image:linear-gradient(180deg,#fefefe,#f2f2f2);background-repeat:repeat-x;box-shadow:0 1px 0 rgba(0,0,0,.05),inset 0 1px 0 hsla(0,0%,100%,.8)}.ts-dropdown.single,.ts-wrapper.single .ts-control{border-color:#b8b8b8}.dropdown-active .ts-control{border-radius:3px 3px 0 0}.ts-dropdown .optgroup-header{font-size:.85em;font-weight:700;padding-top:7px}.ts-dropdown .optgroup{border-top:1px solid #f0f0f0}.ts-dropdown .optgroup:first-child{border-top:0} /*# sourceMappingURL=tom-select.default.min.css.map */ \ No newline at end of file From b713f4fd48a6a2630ce0c2aec77ac81c5cc2fb22 Mon Sep 17 00:00:00 2001 From: mic Date: Sun, 8 Feb 2026 21:34:27 +0100 Subject: [PATCH 37/51] check fixed. see #506 --- js/mzta-compose-script.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/mzta-compose-script.js b/js/mzta-compose-script.js index 3d74322e..6110be63 100644 --- a/js/mzta-compose-script.js +++ b/js/mzta-compose-script.js @@ -630,7 +630,7 @@ switch (message.command) { let textColor = '#333'; let borderColor = '#ccc'; - if (data.spamValue < (data.SpamThreshold || 50)) { + if (data.spamValue >= (data.SpamThreshold || 50)) { bgColor = isDark ? '#5a1a1a' : '#ffe6e6'; textColor = isDark ? '#ffcccc' : '#cc0000'; borderColor = '#cc0000'; @@ -643,7 +643,7 @@ switch (message.command) { container.style.cssText = `background-color: ${bgColor}; color: ${textColor}; border-bottom: 1px solid ${borderColor}; padding: 8px 12px; font-family: system-ui, -apple-system, sans-serif; font-size: 13px; display: flex; align-items: center; gap: 15px; width: 100%; box-sizing: border-box;`; const scoreText = document.createElement('strong'); - scoreText.textContent = ((data.spamValue < (data.SpamThreshold || 50)) ? browser.i18n.getMessage("Spam") : browser.i18n.getMessage("Valid")) + " [" + data.spamValue + "/100]"; + scoreText.textContent = ((data.spamValue >= (data.SpamThreshold || 50)) ? browser.i18n.getMessage("Spam") : browser.i18n.getMessage("Valid")) + " [" + data.spamValue + "/100]"; const reasonText = document.createElement('span'); reasonText.textContent = browser.i18n.getMessage("Explanation") + ": " + data.explanation; From 45cc823171ead0e44d17770595b42ac9982145a5 Mon Sep 17 00:00:00 2001 From: mic Date: Sun, 8 Feb 2026 21:38:22 +0100 Subject: [PATCH 38/51] showing the spam info on the current message when the elaboration is ended. see #506 --- mzta-background.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/mzta-background.js b/mzta-background.js index 886f7384..7f7040e1 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -1283,11 +1283,19 @@ async function processEmails(messages, addTagsAuto, spamFilter) { } taSpamReport.saveReportData(report_data, message.headerMessageId); + + // Check if the message is currently displayed and update the banner + let tabs = await browser.tabs.query({ active: true, currentWindow: true }); + if (tabs.length > 0) { + let activeTab = tabs[0]; + let displayedMessage = await browser.messageDisplay.getDisplayedMessage(activeTab.id); + if (displayedMessage && displayedMessage.id === message.id) { + browser.tabs.sendMessage(activeTab.id, { command: "showSpamReport", data: report_data }); + } + } } } taWorkingStatus.stopWorking(); } - - browser.messages.onNewMailReceived.addListener(newEmailListener, !prefs_init.add_tags_auto_only_inbox); \ No newline at end of file From 3be8fad6dbeed44c7bdbad70cb72393e84c44673 Mon Sep 17 00:00:00 2001 From: mic Date: Sun, 8 Feb 2026 22:01:52 +0100 Subject: [PATCH 39/51] release notes updated --- CHANGELOG.md | 1 + options/mzta-release-notes.html | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc9be316..782bf6dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@
  • [All APIs] Added an option to get a calendar event without selecting some text, but using the full text body of the email [#518].
  • [All APIs] Added a new menu item to create a calendar event from the text saved in the clipboard [#362].
  • Added a button to copy a prompt in the Custom Prompts page [#598].
  • +
  • [All APIs] Showing the spam filter info at the top of the message. The data is saved only for the session in which the message has been checked for spam [#506].
  • ...
  • Version 3.8.3 - 22/01/2026

    diff --git a/options/mzta-release-notes.html b/options/mzta-release-notes.html index d1c22e70..27e2ce80 100644 --- a/options/mzta-release-notes.html +++ b/options/mzta-release-notes.html @@ -23,6 +23,7 @@
  • [All APIs] Added an option to get a calendar event without selecting some text, but using the full text body of the email [#518].
  • [All APIs] Added a new menu item to create a calendar event from the text saved in the clipboard [#362].
  • Added a button to copy a prompt in the Custom Prompts page [#598].
  • +
  • [All APIs] Showing the spam filter info at the top of the message. The data is saved only for the session in which the message has been checked for spam [#506].
  • ...
  • Version 3.8.3 - 22/01/2026

    From 545b6db6d572fc3baabd01186dbbc1eddb8dd776 Mon Sep 17 00:00:00 2001 From: mic Date: Sun, 8 Feb 2026 22:10:44 +0100 Subject: [PATCH 40/51] added a close button for the spam info. see #506 --- js/mzta-compose-script.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/js/mzta-compose-script.js b/js/mzta-compose-script.js index 6110be63..a7ef57de 100644 --- a/js/mzta-compose-script.js +++ b/js/mzta-compose-script.js @@ -648,8 +648,17 @@ switch (message.command) { const reasonText = document.createElement('span'); reasonText.textContent = browser.i18n.getMessage("Explanation") + ": " + data.explanation; + const closeBtn = document.createElement('span'); + closeBtn.textContent = '×'; + closeBtn.style.cssText = 'margin-left: auto; cursor: pointer; font-weight: bold; font-size: 16px; padding: 0 5px;'; + closeBtn.title = browser.i18n.getMessage("chatgpt_win_close"); + closeBtn.onclick = function() { + container.remove(); + }; + container.appendChild(scoreText); container.appendChild(reasonText); + container.appendChild(closeBtn); document.body.insertBefore(container, document.body.firstChild); return Promise.resolve(true); From 7d843c07326f16b1ed07ff63e88a90586edd0192 Mon Sep 17 00:00:00 2001 From: mic Date: Sun, 8 Feb 2026 22:17:31 +0100 Subject: [PATCH 41/51] added an info about data persistence for the spam reports --- _locales/en/messages.json | 4 ++++ pages/spamfilter/mzta-spamfilter.html | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 304a4e50..06d84887 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1363,6 +1363,10 @@ "message": "Spam Filter Reports", "description": "" }, + "SpamReport_infoline": { + "message": "This information is saved only for the current session.", + "description": "" + }, "Date": { "message": "Date", "description": "" diff --git a/pages/spamfilter/mzta-spamfilter.html b/pages/spamfilter/mzta-spamfilter.html index c8ad47f4..209d14c6 100644 --- a/pages/spamfilter/mzta-spamfilter.html +++ b/pages/spamfilter/mzta-spamfilter.html @@ -57,7 +57,8 @@
    -

    __MSG_SpamReport_Title__

    +
    __MSG_SpamReport_Title__ +
    __MSG_SpamReport_infoline__
    From 0550077567595de9f878d6a45e5d9781a6d1e68b Mon Sep 17 00:00:00 2001 From: mic Date: Sun, 8 Feb 2026 22:22:00 +0100 Subject: [PATCH 42/51] when closing the spam info panel, the report is deleted so the info is not displayed again. see #506 --- js/mzta-compose-script.js | 1 + js/mzta-spamreport.js | 5 +++++ mzta-background.js | 3 +++ 3 files changed, 9 insertions(+) diff --git a/js/mzta-compose-script.js b/js/mzta-compose-script.js index a7ef57de..09e64272 100644 --- a/js/mzta-compose-script.js +++ b/js/mzta-compose-script.js @@ -654,6 +654,7 @@ switch (message.command) { closeBtn.title = browser.i18n.getMessage("chatgpt_win_close"); closeBtn.onclick = function() { container.remove(); + browser.runtime.sendMessage({ command: "removeSpamReport", headerMessageId: data.headerMessageId }); }; container.appendChild(scoreText); diff --git a/js/mzta-spamreport.js b/js/mzta-spamreport.js index 99b4efc5..3674cdc0 100644 --- a/js/mzta-spamreport.js +++ b/js/mzta-spamreport.js @@ -32,6 +32,11 @@ export const taSpamReport = { return output[key] || null; }, + async removeReportData(data_id) { + const key = this._data_prefix + data_id; + await browser.storage.session.remove(key); + }, + async getAllReportData() { let allData = await browser.storage.session.get(null); let reportData = {}; diff --git a/mzta-background.js b/mzta-background.js index 7f7040e1..3eb2b66e 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -374,6 +374,9 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => { } _checkSpamReport(sender.tab.id); break; + case 'removeSpamReport': + taSpamReport.removeReportData(message.headerMessageId); + break; default: break; } From b4ae956b82d58365455b841e1dc4736e0eff16e6 Mon Sep 17 00:00:00 2001 From: mic Date: Sun, 8 Feb 2026 22:30:07 +0100 Subject: [PATCH 43/51] added an option to hide the spam filter info in the message. see #506 --- _locales/en/messages.json | 8 ++++++++ mzta-background.js | 19 +++++++++++++------ options/mzta-options-default.js | 1 + pages/spamfilter/mzta-spamfilter.html | 9 +++++++++ 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 06d84887..60a2e279 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1347,6 +1347,14 @@ "message": "If the value returned by the AI is above this threshold, the email will be moved to the spam folder.", "description": "" }, + "prefs_OptionText_spamfilter_show_msg_panel": { + "message": "Show spam report panel", + "description": "" + }, + "prefs_OptionText_spamfilter_show_msg_panel_Info": { + "message": "If checked, a panel with the spam report will be shown on top of the message.", + "description": "" + }, "spamfilter_threshold_too_low": { "message": "The spam threshold is too low! You will likely mark too much mails as spam!", "description": "" diff --git a/mzta-background.js b/mzta-background.js index 3eb2b66e..b0eb1f13 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -359,6 +359,10 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => { browser.tabs.sendMessage(message.tabId, { command: "api_send_custom_text", custom_text: message.custom_text }); break; case 'checkSpamReport': + if(!prefs_init.spamfilter_show_msg_panel){ + return; + } + async function _checkSpamReport(tabId) { try { if (sender.tab.type !== 'messageDisplay' && sender.tab.type !== 'mail') return; @@ -834,6 +838,7 @@ async function reload_pref_init(){ spamfilter: prefs_default.spamfilter, summarize: prefs_default.summarize, spamfilter_threshold: prefs_default.spamfilter_threshold, + spamfilter_show_msg_panel: prefs_default.spamfilter_show_msg_panel, dynamic_menu_force_enter: prefs_default.dynamic_menu_force_enter, ...getDynamicSettingsDefaults(['use_specific_integration', 'connection_type']) }); @@ -1288,12 +1293,14 @@ async function processEmails(messages, addTagsAuto, spamFilter) { taSpamReport.saveReportData(report_data, message.headerMessageId); // Check if the message is currently displayed and update the banner - let tabs = await browser.tabs.query({ active: true, currentWindow: true }); - if (tabs.length > 0) { - let activeTab = tabs[0]; - let displayedMessage = await browser.messageDisplay.getDisplayedMessage(activeTab.id); - if (displayedMessage && displayedMessage.id === message.id) { - browser.tabs.sendMessage(activeTab.id, { command: "showSpamReport", data: report_data }); + if (prefs_init.spamfilter_show_msg_panel) { + let tabs = await browser.tabs.query({ active: true, currentWindow: true }); + if (tabs.length > 0) { + let activeTab = tabs[0]; + let displayedMessage = await browser.messageDisplay.getDisplayedMessage(activeTab.id); + if (displayedMessage && displayedMessage.id === message.id) { + browser.tabs.sendMessage(activeTab.id, { command: "showSpamReport", data: report_data }); + } } } } diff --git a/options/mzta-options-default.js b/options/mzta-options-default.js index 408ad38d..ddeae06f 100644 --- a/options/mzta-options-default.js +++ b/options/mzta-options-default.js @@ -133,6 +133,7 @@ export const prefs_default = { spamfilter: false, spamfilter_threshold: 70, spamfilter_enabled_accounts: [], + spamfilter_show_msg_panel: true, summarize: false, ...generated_prefs } diff --git a/pages/spamfilter/mzta-spamfilter.html b/pages/spamfilter/mzta-spamfilter.html index 209d14c6..4576314e 100644 --- a/pages/spamfilter/mzta-spamfilter.html +++ b/pages/spamfilter/mzta-spamfilter.html @@ -35,6 +35,15 @@ + + + +
    __MSG_prefs_OptionText_spamfilter_show_msg_panel__ + +
    From 32aa24c88d5a9f418894ba0b5ef7f9440d33b0b9 Mon Sep 17 00:00:00 2001 From: Mic Date: Tue, 10 Feb 2026 18:27:40 +0100 Subject: [PATCH 44/51] saving the selection when selected and not when the user clicks the button to prevent recent changes in the ChatGPT web interface. see #646 --- js/mzta-chatgpt.js | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/js/mzta-chatgpt.js b/js/mzta-chatgpt.js index eeda649e..e64d5eb8 100644 --- a/js/mzta-chatgpt.js +++ b/js/mzta-chatgpt.js @@ -30,6 +30,7 @@ let current_mailMessageId = null; let selectionChangeTimeout = null; let isDragging = false; let delay_wait_completion = 7000; // milliseconds +let lastSelectedHtml = ""; async function chatgpt_sendMsg(msg, method ='') { // return -1 send button not found, -2 textarea not found let textArea = document.getElementById('prompt-textarea') @@ -205,6 +206,10 @@ function addCustomDiv(prompt_action,tabId,mailMessageId) { var btn_ok = document.createElement('button'); btn_ok.id="mzta-btn_ok"; btn_ok.classList.add('mzta-btn'); + // Prevent the button from stealing focus and clearing the selection + btn_ok.addEventListener('mousedown', function(e) { + e.preventDefault(); + }); //console.log('>>>>>>>>>>>>>>> default: '+prompt_action) switch(String(prompt_action)){ default: @@ -247,6 +252,10 @@ function addCustomDiv(prompt_action,tabId,mailMessageId) { var btn_change_reply_type = document.createElement('button'); disableButton(btn_change_reply_type); btn_change_reply_type.id = 'mzta-btn_change_reply_type'; + // Prevent the button from stealing focus and clearing the selection + btn_change_reply_type.addEventListener('mousedown', function(e) { + e.preventDefault(); + }); btn_change_reply_type.classList.add('mzta-btn'); btn_change_reply_type.title = browser.i18n.getMessage("chatgpt_win_change_reply_type"); // Create SVG element @@ -320,6 +329,10 @@ function addCustomDiv(prompt_action,tabId,mailMessageId) { var btn_diff = document.createElement('button'); btn_diff.id='mzta-btn_diff'; btn_diff.classList.add('mzta-btn'); + // Prevent the button from stealing focus and clearing the selection + btn_diff.addEventListener('mousedown', function(e) { + e.preventDefault(); + }); btn_diff.textContent = browser.i18n.getMessage('btn_show_differences'); btn_diff.style.display = 'none'; disableButton(btn_diff); @@ -672,7 +685,7 @@ function replaceNewlinesWithBr(node) { function getSelectedHtml() { // Get the Selection object var selection = window.getSelection(); - + // console.log(">>>>>>>>>>>>> getSelectedHtml selection.rangeCount: " + selection.rangeCount); if (selection.rangeCount > 0) { // Get the first selected range var range = selection.getRangeAt(0); @@ -682,10 +695,14 @@ function getSelectedHtml() { // Clone the contents of the range into the temporary div tempDiv.appendChild(range.cloneContents()); - + // console.log(">>>>>>>>>>>>>>>>>> tempDiv.innerHTML: " + tempDiv.innerHTML); // Return the HTML of the selected content return tempDiv.innerHTML.replace(/^

    "/, '

    ').replace(/"<\\/p>$/, '

    '); // strip quotation marks; } + if (lastSelectedHtml) { + // console.log(">>>>>>>>>>>>> getSelectedHtml using cached selection lastSelectedHtml: "+ lastSelectedHtml); + return lastSelectedHtml; + } return ""; } @@ -708,6 +725,14 @@ document.addEventListener("selectionchange", function() { let btn_ok = document.getElementById('mzta-btn_ok'); let btn_diff = document.getElementById('mzta-btn_diff'); if (isSomethingSelected()) { + // Cache the selection + var selection = window.getSelection(); + if (selection.rangeCount > 0) { + var range = selection.getRangeAt(0); + var tempDiv = document.createElement("div"); + tempDiv.appendChild(range.cloneContents()); + lastSelectedHtml = tempDiv.innerHTML.replace(/^

    "/, '

    ').replace(/"<\\/p>$/, '

    '); + } enableButton(btn_ok); if(current_action == '1'){ let btn_reply_type = document.getElementById('mzta-btn_change_reply_type'); @@ -756,7 +781,8 @@ function selectContentOnMouseUp(event) { // If the click was inside the excluded area, do nothing return; } - + // console.log(">>>>>>>>>>>>> selectContentOnMouseUp isDragging: " + isDragging); + // Reset the dragging flag when the mouse is released") if ((!isDragging)&&(!isSomethingSelected())) { // If no dragging has occurred, execute the selection code selectContentOnClick(event); @@ -781,6 +807,8 @@ function selectContentOnClick(event) { // Traverse the DOM upwards to find the nearest parent div var parentDiv = clickedElement.closest('div'); + // console.log(">>>>>>>>>>>>> parentDiv: " + parentDiv.classList); + if (parentDiv) { // Create a range object var range = document.createRange(); @@ -796,6 +824,7 @@ function selectContentOnClick(event) { // Add the new range to the selection selection.addRange(range); + // console.log(">>>>>>>>>>>>> selectContentOnClick selection.rangeCount: " + selection.rangeCount); } } From e4e356de0db9163fb4e21ff0ecabfc96f90b330d Mon Sep 17 00:00:00 2001 From: Mic Date: Tue, 10 Feb 2026 18:28:17 +0100 Subject: [PATCH 45/51] version set to 3.8.4 and release notes updated --- CHANGELOG.md | 4 ++++ manifest.json | 2 +- options/mzta-release-notes.html | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31d24e43..bdfdc8c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ +

    Version 3.8.4 - 10/02/2026

    +
      +
    • [ChatGPT Web] Fix: Correctly importing the selected text into the compose windows also when ChatGPT shows the advanced mail editor in the response [#646].
    • +

    Version 3.8.3 - 22/01/2026

    • Fix: Correctly saving the API settings in new custom prompts [#623].
    • diff --git a/manifest.json b/manifest.json index c70f8713..5c77e980 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "ThunderAI", "description": "__MSG_extensionDescription__", - "version": "3.8.3", + "version": "3.8.4", "author": "Mic (m@micz.it)", "homepage_url": "https://micz.it/thunderbird-addon-thunderai/", "browser_specific_settings": { diff --git a/options/mzta-release-notes.html b/options/mzta-release-notes.html index c87ef83c..33f60cf8 100644 --- a/options/mzta-release-notes.html +++ b/options/mzta-release-notes.html @@ -7,6 +7,10 @@

      ThunderAI Release Notes

      +

      Version 3.8.4 - 10/02/2026

      +
        +
      • [ChatGPT Web] Fix: Correctly importing the selected text into the compose windows also when ChatGPT shows the advanced mail editor in the response [#646].
      • +

      Version 3.8.3 - 22/01/2026

      • Fix: Correctly saving the API settings in new custom prompts [#623].
      • From edd22529a25f00deeedbc7f7594e9ffe4bcad13f Mon Sep 17 00:00:00 2001 From: Luna Jernberg Date: Wed, 11 Feb 2026 06:16:19 +0100 Subject: [PATCH 46/51] Modify Swedish contributors in README.md Updated Swedish translation contributors in README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 617a96ee..1d4382fd 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ Are you using this addon in your Thunderbird? - Português Brasileiro (pt-br): Bruno Pereira de Souza - Russian (ru): [Maksim](https://hosted.weblate.org/user/law820314/) - Spanish (es): [Gerardo Sobarzo](https://hosted.weblate.org/user/gerardo.sobarzo/), [Andrés Rendón Hernández](https://hosted.weblate.org/user/arendon/), [Erick Limon](https://hosted.weblate.org/user/ErickLimonG/) -- Swedish (sv): [Andreas Pettersson](https://hosted.weblate.org/user/Andy_tb/) +- Swedish (sv): [Andreas Pettersson](https://hosted.weblate.org/user/Andy_tb/) , [Luna Jernberg] (https://hosted.weblate.org/user/bittin1ddc447d824349b2/)
        Do you want to help translate this addon? [Find out how!](https://micz.it/thunderbird-addon-thunderai/translate/)
        From fc578c42a40c5acc8f11cf8e631a4195da333e2f Mon Sep 17 00:00:00 2001 From: bittin1ddc447d824349b2 Date: Wed, 11 Feb 2026 06:15:06 +0100 Subject: [PATCH 47/51] Translated using Weblate (Swedish) Currently translated at 100.0% (468 of 468 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/sv/ --- _locales/sv/messages.json | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/_locales/sv/messages.json b/_locales/sv/messages.json index 5ac1a1b6..158be7fe 100644 --- a/_locales/sv/messages.json +++ b/_locales/sv/messages.json @@ -1403,5 +1403,14 @@ }, "Valid": { "message": "Giltig" + }, + "prefs_OptionText_spamfilter_show_msg_panel": { + "message": "Visa panelen för skräppostrapportering" + }, + "prefs_OptionText_spamfilter_show_msg_panel_Info": { + "message": "Om markerat visas en panel med skräppostrapportering högst upp av meddelandet." + }, + "SpamReport_infoline": { + "message": "Denna information sparas endast för den aktuella sessionen." } } From 378395d324b2530d7def28694537bab3bf1517c7 Mon Sep 17 00:00:00 2001 From: Mic Date: Wed, 11 Feb 2026 21:19:48 +0100 Subject: [PATCH 48/51] Fix link formatting for Swedish translation entry --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 88815e8b..e4847667 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ Are you using this addon in your Thunderbird? - Português Brasileiro (pt-br): Bruno Pereira de Souza - Russian (ru): [Maksim](https://hosted.weblate.org/user/law820314/) - Spanish (es): [Gerardo Sobarzo](https://hosted.weblate.org/user/gerardo.sobarzo/), [Andrés Rendón Hernández](https://hosted.weblate.org/user/arendon/), [Erick Limon](https://hosted.weblate.org/user/ErickLimonG/) -- Swedish (sv): [Andreas Pettersson](https://hosted.weblate.org/user/Andy_tb/) , [Luna Jernberg] (https://hosted.weblate.org/user/bittin1ddc447d824349b2/) +- Swedish (sv): [Andreas Pettersson](https://hosted.weblate.org/user/Andy_tb/) , [Luna Jernberg](https://hosted.weblate.org/user/bittin1ddc447d824349b2/)
        Do you want to help translate this addon? [Find out how!](https://micz.it/thunderbird-addon-thunderai/translate/)
        From 5fcf21a5e974b87a347ca25592dfb569dfe7f0e4 Mon Sep 17 00:00:00 2001 From: mic Date: Wed, 11 Feb 2026 21:21:21 +0100 Subject: [PATCH 49/51] indentation fixed --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0dd20ac..dc8b453f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@
      • Added a button to copy a prompt in the Custom Prompts page [#598].
      • [All APIs] Showing the spam filter info at the top of the message. The data is saved only for the session in which the message has been checked for spam [#506].
      • ...
      • -
      +

    Version 3.8.4 - 10/02/2026

    • [ChatGPT Web] Fix: Correctly importing the selected text into the compose windows also when ChatGPT shows the advanced mail editor in the response [#646].
    • From c764a1ee2f21cfd49727cb03f76ec14e0f51378a Mon Sep 17 00:00:00 2001 From: Andreas Pettersson Date: Wed, 11 Feb 2026 17:30:39 +0100 Subject: [PATCH 50/51] Translated using Weblate (Swedish) Currently translated at 100.0% (468 of 468 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/sv/ --- _locales/sv/messages.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_locales/sv/messages.json b/_locales/sv/messages.json index 158be7fe..f447b3d8 100644 --- a/_locales/sv/messages.json +++ b/_locales/sv/messages.json @@ -1408,7 +1408,7 @@ "message": "Visa panelen för skräppostrapportering" }, "prefs_OptionText_spamfilter_show_msg_panel_Info": { - "message": "Om markerat visas en panel med skräppostrapportering högst upp av meddelandet." + "message": "Om markerat visas en panel med skräppostrapporten högst upp i meddelandet." }, "SpamReport_infoline": { "message": "Denna information sparas endast för den aktuella sessionen." From 73d1795dc75c4f8ece2f55af33470e4972ee9d82 Mon Sep 17 00:00:00 2001 From: Christos Kanotidis Date: Wed, 11 Feb 2026 21:15:20 +0100 Subject: [PATCH 51/51] Translated using Weblate (Greek) Currently translated at 100.0% (468 of 468 strings) Translation: ThunderAI for Thunderbird/main Translate-URL: https://hosted.weblate.org/projects/thunderai/main/el/ --- _locales/el/messages.json | 126 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/_locales/el/messages.json b/_locales/el/messages.json index c240bfd7..5f6e056f 100644 --- a/_locales/el/messages.json +++ b/_locales/el/messages.json @@ -1286,5 +1286,131 @@ }, "prefs_anthropic_temperature_Info": { "message": "Ποσότητα τυχαιότητας που εισάγεται στην απόκριση. Προεπιλογή 1,0. Εύρος από 0,0 έως 1,0. Χρησιμοποιήστε θερμοκρασία πιο κοντά στο 0,0 για αναλυτική ανάλυση." + }, + "placeholder_mail_text_body_or_selected": { + "message": "Σώμα μηνύματος ή επιλεγμένο κείμενο" + }, + "placeholder_mail_html_body_or_selected": { + "message": "Σώμα μηνύματος ή επιλεγμένη HTML" + }, + "prompt_get_calendar_event_from_clipboard": { + "message": "Προσθήκη συμβάντος ημερολογίου από το πρόχειρο" + }, + "clipboard_read_error": { + "message": "Δεν ήταν δυνατή η ανάγνωση του προχείρου. Ελέγξτε τα δικαιώματα." + }, + "clipboard_empty_error": { + "message": "Το πρόχειρο είναι άδειο. Παρακαλώ αντιγράψτε πρώτα κάποιο κείμενο." + }, + "clipboard_permission_denied": { + "message": "Η άδεια χρήσης του πρόχειρου απορρίφθηκε. Ενεργοποιήστε ξανά τη λειτουργία στις ρυθμίσεις για να παραχωρήσετε άδεια." + }, + "clipboard_permission_error": { + "message": "Σφάλμα κατά την αίτηση άδειας στο πρόχειρο. Δοκιμάστε ξανά." + }, + "prefs_OptionText_get_calendar_event_from_clipboard": { + "message": "Λήψη συμβάντος ημερολογίου από το πρόχειρο" + }, + "prefs_OptionText_get_calendar_event_from_clipboard_Info": { + "message": "Εμφάνιση ενός επιπλέον στοιχείου μενού για τη δημιουργία συμβάντων ημερολογίου από περιεχόμενο κειμένου στο πρόχειρο." + }, + "Summarize_prompt_prefs_title": { + "message": "Προσθήκη επιλογών σύνοψης" + }, + "prompt_summarize": { + "message": "Συνοψίστε αυτό το μήνυμα ηλεκτρονικού ταχυδρομείου ή αυτά τα μηνύματα ηλεκτρονικού ταχυδρομείου" + }, + "prompt_summarize_full_text": { + "message": "Είστε βοηθός που συνοψίζει συνομιλίες μέσω email. \nΔεδομένου ενός νήματος email, δημιουργήστε μια συνοπτική και ακριβή περίληψη που να καταγράφει: \n- Το κύριο θέμα ή τον σκοπό της συζήτησης \n- Βασικές αποφάσεις, συμπεράσματα ή συμφωνίες \n- Σημαντικές ερωτήσεις, αιτήματα ή ενέργειες \n- Ποιος είναι υπεύθυνος για κάθε ενέργεια (εάν αναφέρεται) \nΠαραλείψτε χαιρετισμούς, υπογραφές, παρατιθέμενο κείμενο και περιττές ανταλλαγές απόψεων. Μην προσθέτετε υποθέσεις ή πληροφορίες που δεν υπάρχουν στα email. \nΓράψτε την περίληψη σε σαφή, ουδέτερη γλώσσα, κατάλληλη για έναν πολυάσχολο επαγγελματία." + }, + "prompt_summarize_email_template": { + "message": "Σύνοψη προτύπου email" + }, + "prompt_summarize_email_template_full_text": { + "message": "Από: {%author%} \nΠρος: {%recipients%} \nΚοινοποίηση: {%cc_list%} \nΘέμα: {%mail_subject%} \nΗμερομηνία: {%mail_datetime%} \nΣυνημμένα: {%mail_attachments_info%} \nΚύριο κείμενο: \n{%mail_text_body%}" + }, + "prompt_summarize_email_separator": { + "message": "Διαχωριστής email" + }, + "prompt_summarize_email_separator_full_text": { + "message": "\n\n---------- ΕΠΟΜΕΝΟ EMAIL -----------\n\n" + }, + "prefs_OptionText_Summarize_infoline2": { + "message": "Μπορείτε να αλλάξετε την προτροπή όπως επιθυμείτε. Το πρώτο πεδίο είναι η κύρια προτροπή, το δεύτερο πεδίο είναι το πρότυπο για ένα μόνο μήνυμα ηλεκτρονικού ταχυδρομείου. Η λίστα των email θα προσαρτηθεί στην κύρια προτροπή. Τα email θα διαχωρίζονται με το διαχωριστικό που καθορίζεται στο τρίτο πεδίο." + }, + "prefs_OptionText_Summarize_main_prompt": { + "message": "Η κύρια προτροπή που περιγράφει την εργασία που θα εκτελεστεί σε όλα τα επιλεγμένα email:" + }, + "prefs_OptionText_Summarize_email_template": { + "message": "Το πρότυπο για ένα μόνο email:" + }, + "prefs_OptionText_Summarize_email_separator": { + "message": "Το διαχωριστικό μεταξύ των email:" + }, + "prefs_OptionText_get_calendar_event_use_specific_integration_Info": { + "message": "Εάν επιλεγεί, το μοντέλο και το API που καθορίζονται παρακάτω θα χρησιμοποιηθούν για τη δημιουργία συμβάντων ημερολογίου, ανεξάρτητα από αυτό που έχει επιλεγεί στη σελίδα επιλογών ThunderAI." + }, + "prefs_OptionText_summarize": { + "message": "Σύνοψη αλληλογραφίας" + }, + "prefs_OptionText_summarize_use_specific_integration_Info": { + "message": "Εάν επιλεγεί, το μοντέλο και το API που καθορίζονται παρακάτω θα χρησιμοποιηθούν για τη σύνοψη email(s), ανεξάρτητα από αυτό που έχει επιλεγεί στη σελίδα επιλογών ThunderAI." + }, + "prefs_OptionText_summarize_Info": { + "message": "Εάν είναι επιλεγμένο, προσθέτει μια επιλογή στο μενού περιβάλλοντος για τη σύνοψη της αλληλογραφίας." + }, + "prefs_OptionText_btnManageSummarizeInfo": { + "message": "Διαχείριση ρυθμίσεων σύνοψης" + }, + "Summarize_PageTitle": { + "message": "Διαχείριση ρυθμίσεων σύνοψης" + }, + "Summarize_info_default": { + "message": "Σε αυτήν τη σελίδα μπορείτε να τροποποιήσετε την προεπιλεγμένη προτροπή που χρησιμοποιείται για τη σύνοψη των μηνυμάτων ηλεκτρονικού ταχυδρομείου." + }, + "Summarize_prompt_text_title": { + "message": "Τρέχον κείμενο προτροπής" + }, + "prefs_OptionText_spamfilter_show_msg_panel": { + "message": "Εμφάνιση πλαισίου αναφοράς ανεπιθύμητων μηνυμάτων" + }, + "prefs_OptionText_spamfilter_show_msg_panel_Info": { + "message": "Εάν είναι επιλεγμένο, θα εμφανίζεται ένα πλαίσιο με την αναφορά ανεπιθύμητης αλληλογραφίας στο επάνω μέρος του μηνύματος." + }, + "SpamReport_infoline": { + "message": "Αυτές οι πληροφορίες αποθηκεύονται μόνο για την τρέχουσα συνεδρία." + }, + "Spam": { + "message": "Ανεπιθύμητα μηνύματα" + }, + "Valid": { + "message": "Έγκυρα Μηνύματα" + }, + "context_menu_mzta-summarize": { + "message": "Σύνοψη" + }, + "CORS_alternative_2_new": { + "message": "Πατήστε το παρακάτω κουμπί για να δώσετε άδεια στον τρέχοντα κεντρικό υπολογιστή να αποφύγει οποιοδήποτε πρόβλημα με το CORS." + }, + "CORS_give_host_perm": { + "message": "Δώστε άδεια στον τρέχοντα κεντρικό υπολογιστή" + }, + "CORS_localhost_warn": { + "message": "Εάν χρησιμοποιείτε localhost ή 127.0.0.1 επειδή ο διακομιστής AI φιλοξενείται στον υπολογιστή σας, απαιτείται το δικαίωμα ." + }, + "customPrompts_export_include_api_settings": { + "message": "Θέλετε να συμπεριλάβετε τις ρυθμίσεις API στην εξαγωγή; Λάβετε υπόψη ότι και το Κλειδί API θα αποθηκευτεί στο αρχείο!" + }, + "prefs_OptionText_calendar_no_selection": { + "message": "Μην ζητάτε να επιλέξετε κείμενο" + }, + "prefs_OptionText_calendar_no_selection_Info": { + "message": "Εάν είναι επιλεγμένο, δεν χρειάζεται να επιλέξετε κάποιο κείμενο. Το πλήρες σώμα του μηνύματος θα χρησιμοποιηθεί για τη λήψη του συμβάντος ημερολογίου." + }, + "customPrompts_btnCopy": { + "message": "Αντιγραφή" + }, + "copy_text": { + "message": "αντιγραφή" } }