From 774c63ef9eef207fd9aad8fb581968c0dc0a829f Mon Sep 17 00:00:00 2001 From: Mic Date: Thu, 2 Apr 2026 00:23:00 +0200 Subject: [PATCH 01/37] new menus first try. see #680 --- _locales/en/messages.json | 20 ++ claude-spec/02-prompts.md | 21 +- js/mzta-menus.js | 60 +++++- js/mzta-prompts.js | 38 +++- js/mzta-utils.js | 18 ++ mzta-background.js | 114 ++--------- pages/customprompts/mzta-custom-prompts.html | 9 + pages/customprompts/mzta-custom-prompts.js | 42 +++- popup/mzta-popup.js | 204 ++----------------- 9 files changed, 239 insertions(+), 287 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index dfbd3656..36078e43 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -203,6 +203,26 @@ "message": "Composing an email", "description": "" }, + "show_in": { + "message": "Show in", + "description": "" + }, + "show_in_popup": { + "message": "Popup only", + "description": "" + }, + "show_in_context": { + "message": "Context menu only", + "description": "" + }, + "show_in_both": { + "message": "Both", + "description": "" + }, + "show_in_none": { + "message": "None", + "description": "" + }, "customPrompts_close_button": { "message": "Close button", "description": "" diff --git a/claude-spec/02-prompts.md b/claude-spec/02-prompts.md index 60b1624f..efce3539 100644 --- a/claude-spec/02-prompts.md +++ b/claude-spec/02-prompts.md @@ -28,9 +28,10 @@ Prompts are the core user-facing feature of ThunderAI. Each prompt defines an AI | Property | Type | Description | |----------|------|-------------| -| `enabled` | number | `0` = hidden, `1` = shown in popup | -| `position_display` | number | Sort order in reading view | -| `position_compose` | number | Sort order in compose view | +| `enabled` | number | `0` = hidden, `1` = shown in menus | +| `position_display` | number | Sort order in reading view (used when alphabetical ordering is off) | +| `position_compose` | number | Sort order in compose view (used when alphabetical ordering is off) | +| `show_in` | string | `"popup"` = popup only, `"context"` = context menu only, `"both"` = both, `"none"` = hidden from all menus. Default: `"popup"` for default/custom prompts, `"both"` for special prompts | ### Per-Prompt API Override Properties @@ -59,6 +60,20 @@ Some prompts trigger additional Thunderbird actions beyond just sending text to These special prompts can have their own dedicated API integration settings (configured in the Options page). The list of these special prompts is in `options/mzta-options-default.js` as `special_prompts_with_integration`. +## Menu System + +### Popup Menu +- Displays prompts filtered by `show_in` (`"popup"` or `"both"`) and by tab context (`type` property) +- Ordering: user can choose between alphabetical or position-based (using `position_display`/`position_compose`) via the `dynamic_menu_order_alphabet` preference +- Special prompts retain their colored background (CSS class `special_prompt`) in the popup based on `is_special == "1"` + +### Context Menu +- Dynamically built from all prompts with `show_in` set to `"context"` or `"both"`, filtered to reading types only (`type` 0 or 1) +- Appears as a "ThunderAI" submenu in the `message_list` context +- Special prompts (add_tags, spamfilter, summarize, translate) route through `processEmails()` for batch processing; regular prompts execute via `menus.executeMenuAction()` +- Icons: special prompts use dedicated icons (defined in `contextMenuIconsPath`); all other prompts use the addon icon (`images/icon-32.png`) +- Add Tags in context menu assigns tags automatically (`addTagsAuto: true`), while in the popup it shows the interactive tag selection form + ### Summarize: Dual-Mode Prompt System The summarize feature uses two distinct prompt pathways: diff --git a/js/mzta-menus.js b/js/mzta-menus.js index 1c86f99e..1cea4993 100644 --- a/js/mzta-menus.js +++ b/js/mzta-menus.js @@ -35,6 +35,7 @@ import { cleanupNewlines, checkIfTagLabelExists, getConnectionType, + getContextMenuIcon, } from './mzta-utils.js' import { taPromptUtils } from './mzta-utils-prompt.js'; import { taLogger } from './mzta-logger.js'; @@ -74,8 +75,7 @@ export class mzta_Menus { this.rootMenu = []; this.shortcutMenu = []; this.menu_listeners = {}; - this.allPrompts = await getPrompts(true,also_special); - this.allPrompts.sort((a, b) => a.name.localeCompare(b.name)); + this.allPrompts = await getPrompts(true,also_special); this.allPrompts.forEach((prompt) => { this.addAction(prompt) }); @@ -480,15 +480,69 @@ export class mzta_Menus { } addShortcutMenu(prompt) { - let curr_menu_entry = {id: prompt.id, label: i18nConditionalGet(prompt.name), type: prompt.type}; + let curr_menu_entry = { + id: prompt.id, + label: i18nConditionalGet(prompt.name), + type: prompt.type, + show_in: prompt.show_in || "popup", + is_special: prompt.is_special, + position_display: prompt.position_display, + position_compose: prompt.position_compose, + }; this.shortcutMenu.push(curr_menu_entry); } + async loadContextMenus() { + await browser.menus.removeAll(); + const contextPrompts = this.allPrompts.filter(p => { + const showIn = p.show_in || "popup"; + // Only show prompts that should appear in context menu and are for reading context (type 0 or 1) + return (showIn === "context" || showIn === "both") && (String(p.type) === "0" || String(p.type) === "1"); + }); + + if (contextPrompts.length === 0) { + this.logger.log("No prompts for context menu"); + return; + } + + // Create parent menu + await new Promise(resolve => + browser.menus.create({ + id: 'mzta-context-parent', + title: 'ThunderAI', + contexts: ["message_list"], + }, resolve) + ); + + // Sort alphabetically for context menu + contextPrompts.sort((a, b) => { + const nameA = i18nConditionalGet(a.name); + const nameB = i18nConditionalGet(b.name); + return nameA.localeCompare(nameB); + }); + + // Create child menu items + for (const prompt of contextPrompts) { + const title = i18nConditionalGet(prompt.name); + await new Promise(resolve => + browser.menus.create({ + id: 'mzta-ctx-' + prompt.id, + title: title, + contexts: ["message_list"], + parentId: 'mzta-context-parent', + icons: getContextMenuIcon(prompt.id), + }, resolve) + ); + } + this.logger.log("Context menus loaded: " + contextPrompts.length + " items"); + } + async loadMenus(also_special = []) { await this.initialize(also_special); await this.addMenu(this.rootMenu); this.addClickListener(); this.loadShortcutMenu(); + await this.loadContextMenus(); this.logger.log("Menus loaded"); } diff --git a/js/mzta-prompts.js b/js/mzta-prompts.js index 57b14678..089f7184 100644 --- a/js/mzta-prompts.js +++ b/js/mzta-prompts.js @@ -62,6 +62,12 @@ summaryTabId (set by _openSummaryWebchat in mzta-background.js): The tab ID of the message display tab to update with the saved summary. + Show in menu (show_in attribute): + "popup": Show only in the popup menu + "context": Show only in the context menu + "both": Show in both popup and context menus + "none": Do not show in any menu + ================ USER PROPERTIES Enabled (enabled attribute): 0: Disabled @@ -108,6 +114,7 @@ const defaultPrompts = [ api_type: '', is_default: "1", is_special: "0", + show_in: "popup", }, { id: 'prompt_reply_advanced', @@ -126,6 +133,7 @@ const defaultPrompts = [ api_type: '', is_default: "1", is_special: "0", + show_in: "popup", }, { id: 'prompt_reply_custom_command', @@ -144,6 +152,7 @@ const defaultPrompts = [ api_type: '', is_default: "1", is_special: "0", + show_in: "popup", }, { id: 'prompt_rewrite_polite', @@ -162,6 +171,7 @@ const defaultPrompts = [ api_type: '', is_default: "1", is_special: "0", + show_in: "popup", }, { id: 'prompt_rewrite_formal', @@ -180,6 +190,7 @@ const defaultPrompts = [ api_type: '', is_default: "1", is_special: "0", + show_in: "popup", }, { id: 'prompt_classify', @@ -198,6 +209,7 @@ const defaultPrompts = [ api_type: '', is_default: "1", is_special: "0", + show_in: "popup", }, { id: 'prompt_summarize_this', @@ -216,6 +228,7 @@ const defaultPrompts = [ api_type: '', is_default: "1", is_special: "0", + show_in: "popup", }, { id: 'prompt_proofread_this', @@ -234,6 +247,7 @@ const defaultPrompts = [ api_type: '', is_default: "1", is_special: "0", + show_in: "popup", }, { id: 'prompt_this', @@ -252,6 +266,7 @@ const defaultPrompts = [ api_type: '', is_default: "1", is_special: "0", + show_in: "popup", }, ]; @@ -270,6 +285,7 @@ const specialPrompts = [ api_type: '', is_default: "1", is_special: "1", + show_in: "both", }, { id: 'prompt_get_calendar_event', @@ -285,6 +301,7 @@ const specialPrompts = [ api_type: '', is_default: "1", is_special: "1", + show_in: "both", }, { id: 'prompt_get_calendar_event_from_clipboard', @@ -300,6 +317,7 @@ const specialPrompts = [ api_type: '', is_default: "1", is_special: "1", + show_in: "both", }, { id: 'prompt_get_task', @@ -315,6 +333,7 @@ const specialPrompts = [ api_type: '', is_default: "1", is_special: "1", + show_in: "both", }, { id: 'prompt_spamfilter', @@ -330,6 +349,7 @@ const specialPrompts = [ api_type: '', is_default: "1", is_special: "1", + show_in: "both", }, { id: 'prompt_summarize', @@ -346,6 +366,7 @@ const specialPrompts = [ api_model: '', is_default: "1", is_special: "1", + show_in: "both", }, { id: 'prompt_summarize_email_template', @@ -362,6 +383,7 @@ const specialPrompts = [ api_model: '', is_default: "1", is_special: "1", + show_in: "both", }, { id: 'prompt_summarize_email_separator', @@ -378,6 +400,7 @@ const specialPrompts = [ api_model: '', is_default: "1", is_special: "1", + show_in: "both", }, { id: 'prompt_translate_this', @@ -394,6 +417,7 @@ const specialPrompts = [ api_model: '', is_default: "1", is_special: "1", + show_in: "both", } ]; @@ -464,7 +488,7 @@ export function preparePromptsForExport(prompts, include_api_settings = false){ } if(prompt.is_default == 1){ - let allowedKeys = ['id', 'enabled', 'position_compose', 'position_display', 'need_custom_text']; + let allowedKeys = ['id', 'enabled', 'position_compose', 'position_display', 'need_custom_text', 'show_in']; if(include_api_settings){ allowedKeys.push('api_type'); for (const [integration, options] of Object.entries(integration_options_config)) { @@ -534,6 +558,7 @@ async function getDefaultPrompts_withProps() { prompt.chatgpt_web_project = prefs._default_prompts_properties[prompt.id].chatgpt_web_project; prompt.chatgpt_web_custom_gpt = (prefs._default_prompts_properties[prompt.id]?.chatgpt_web_custom_gpt || '').trim(); prompt.api_type = (prefs._default_prompts_properties[prompt.id]?.api_type || '').trim(); + prompt.show_in = prefs._default_prompts_properties[prompt.id]?.show_in || prompt.show_in; }else{ prompt.position_display = pos; prompt.position_compose = pos; @@ -569,6 +594,9 @@ async function getCustomPrompts() { if(prompt.api_type === undefined){ prompt.api_type = ""; } + if(prompt.show_in === undefined){ + prompt.show_in = "popup"; + } }); return prefs._custom_prompt; } @@ -586,6 +614,7 @@ export async function setDefaultPromptsProperties(prompts) { chatgpt_web_project: (prompt.chatgpt_web_project === undefined || prompt.chatgpt_web_project === "undefined") ? "" : prompt.chatgpt_web_project, chatgpt_web_custom_gpt: (prompt.chatgpt_web_custom_gpt === undefined || prompt.chatgpt_web_custom_gpt === "undefined") ? "" : prompt.chatgpt_web_custom_gpt, api_type: (prompt.api_type === undefined || prompt.api_type === "undefined") ? "" : prompt.api_type, + show_in: (prompt.show_in === undefined || prompt.show_in === "undefined") ? "popup" : prompt.show_in, }; }); //console.log('>>>>>>>>>>>>>> default_prompts_properties: ' + JSON.stringify(default_prompts_properties)); @@ -616,6 +645,13 @@ export async function getSpecialPrompts(){ updatedPrompts.push(newPrompt); } }); + // Migrate: add show_in if missing from saved special prompts + updatedPrompts.forEach((prompt) => { + if (prompt.show_in === undefined) { + prompt.show_in = "both"; + } + }); + // console.log(">>>>>>>>>>>>> getSpecialPrompts updatedPrompts: " + JSON.stringify(updatedPrompts)); if (updatedPrompts.length !== prefs._special_prompts.length) { await browser.storage.local.set({ _special_prompts: updatedPrompts }); diff --git a/js/mzta-utils.js b/js/mzta-utils.js index ce5dc9d4..9859f7f5 100644 --- a/js/mzta-utils.js +++ b/js/mzta-utils.js @@ -35,6 +35,24 @@ export const contextMenuIconsPath = { [contextMenuID_Translate]: 'moz-extension:images/menu_translate.png', }; +// Map from special prompt IDs to context menu IDs +export const specialPromptToContextMenuID = { + 'prompt_add_tags': contextMenuID_AddTags, + 'prompt_spamfilter': contextMenuID_Spamfilter, + 'prompt_summarize': contextMenuID_Summarize, + 'prompt_translate_this': contextMenuID_Translate, +}; + +const defaultContextMenuIcon = 'moz-extension:images/icon-32.png'; + +export function getContextMenuIcon(promptId) { + const contextMenuId = specialPromptToContextMenuID[promptId]; + if (contextMenuId && contextMenuIconsPath[contextMenuId]) { + return contextMenuIconsPath[contextMenuId]; + } + return defaultContextMenuIcon; +} + export function getLanguageDisplayName(languageCode) { const languageDisplay = new Intl.DisplayNames([languageCode], {type: 'language'}); let lang_string = languageDisplay.of(languageCode); diff --git a/mzta-background.js b/mzta-background.js index 7a4b0a27..86555154 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -42,18 +42,12 @@ import { getMessages, getMailBody, extractJsonObject, - contextMenuID_AddTags, - contextMenuID_Spamfilter, - contextMenuID_Summarize, - contextMenuID_Translate, - contextMenuIconsPath, sanitizeChatGPTModelData, sanitizeChatGPTWebCustomData, stripHtmlKeepLines, htmlBodyToPlainText, convertNewlinesToParagraphs, getConnectionType, - checkAPIIntegration, hasSpecificIntegration, } from './js/mzta-utils.js'; import { taPromptUtils } from './js/mzta-utils-prompt.js'; @@ -1446,9 +1440,7 @@ function setupStorageChangeListener() { menus.reload(special_prompts_ids); } - reload_pref_init().then(() => { - addContextMenuItems(); - }); + reload_pref_init(); } }); } @@ -1480,94 +1472,30 @@ setupPermissionsRemovedListener(); const menus = new mzta_Menus(openChatGPT, prefs_init.do_debug); menus.loadMenus(special_prompts_ids); -// Context Menus -function addContextMenu(menu_id) { - browser.menus.remove(menu_id); - browser.menus.create({ - id: menu_id, - title: browser.i18n.getMessage("context_menu_" + menu_id), - contexts: ["message_list"], - icons: contextMenuIconsPath[menu_id], - }); - taLog.log("Context menu added: " + menu_id); - // console.log(">>>>>>> contextMenuIconsPath[menu_id]: " + contextMenuIconsPath[menu_id]); -} +// Context menu click handling +// Context menus are now created dynamically by mzta_Menus.loadContextMenus() +// based on each prompt's show_in property. The menu item IDs use the format 'mzta-ctx-'. +// Special prompts (add_tags, spamfilter, summarize, translate) are routed to processEmails() +// for batch processing. Regular prompts are executed via menus.executeMenuAction(). -function removeContextMenu(menu_id) { - browser.menus.remove(menu_id); - taLog.log("Context menu removed: " + menu_id); -} +const specialContextMenuActions = { + 'prompt_add_tags': (messages) => processEmails({ messages, addTagsAuto: true }), + 'prompt_spamfilter': (messages) => processEmails({ messages, spamFilter: true }), + 'prompt_summarize': (messages) => processEmails({ messages, summarize: true }), + 'prompt_translate_this': (messages) => processEmails({ messages, translate: true }), +}; -function addContextMenuItems() { - let itemsToAdd = []; +browser.menus.onClicked.addListener((info, tab) => { + const menuItemId = info.menuItemId; + if (typeof menuItemId !== 'string' || !menuItemId.startsWith('mzta-ctx-')) { + return; + } + const promptId = menuItemId.replace('mzta-ctx-', ''); - // Add Context menu: Add tags - if(prefs_init.add_tags && checkAPIIntegration(prefs_init.connection_type, prefs_init.add_tags_use_specific_integration,prefs_init.add_tags_connection_type)){ - itemsToAdd.push(contextMenuID_AddTags); + if (specialContextMenuActions[promptId]) { + specialContextMenuActions[promptId](getMessages(info.selectedMessages)); } else { - removeContextMenu(contextMenuID_AddTags); - } - - // Add Context menu: Spamfilter - if(prefs_init.spamfilter && checkAPIIntegration(prefs_init.connection_type, prefs_init.spamfilter_use_specific_integration,prefs_init.spamfilter_connection_type)){ - itemsToAdd.push(contextMenuID_Spamfilter); - } else { - removeContextMenu(contextMenuID_Spamfilter); - } - - // Add Context menu: Summarize - if(prefs_init.summarize && checkAPIIntegration(prefs_init.connection_type, prefs_init.summarize_use_specific_integration, prefs_init.summarize_connection_type)) { - itemsToAdd.push(contextMenuID_Summarize); - } else { - removeContextMenu(contextMenuID_Summarize); - } - - // Add Context menu: Translate - if(prefs_init.translate && checkAPIIntegration(prefs_init.connection_type, prefs_init.translate_use_specific_integration, prefs_init.translate_connection_type)){ - itemsToAdd.push(contextMenuID_Translate); - } else { - removeContextMenu(contextMenuID_Translate); - } - - itemsToAdd.sort((a, b) => { - let titleA = browser.i18n.getMessage("context_menu_" + a); - let titleB = browser.i18n.getMessage("context_menu_" + b); - return titleA.localeCompare(titleB); - }); - - itemsToAdd.forEach(menu_id => { - addContextMenu(menu_id); - }); -} - -addContextMenuItems(); - -// Listen for context menu item clicks -browser.menus.onClicked.addListener( (info, tab) => { - let _add_tags = false - let _spamfilter = false - let _summarize = false; - let _translate = false; - if(info.menuItemId === contextMenuID_AddTags){ - _add_tags = true; - } - if(info.menuItemId === contextMenuID_Spamfilter){ - _spamfilter = true; - } - if(info.menuItemId === contextMenuID_Summarize) { - _summarize = true; - } - if(info.menuItemId === contextMenuID_Translate) { - _translate = true; - } - if(_add_tags || _spamfilter || _summarize || _translate){ - processEmails({ - messages: getMessages(info.selectedMessages), - addTagsAuto: _add_tags, - spamFilter: _spamfilter, - summarize: _summarize, - translate: _translate - }); + menus.executeMenuAction(promptId); } }); diff --git a/pages/customprompts/mzta-custom-prompts.html b/pages/customprompts/mzta-custom-prompts.html index 34622d5d..9351ea3f 100644 --- a/pages/customprompts/mzta-custom-prompts.html +++ b/pages/customprompts/mzta-custom-prompts.html @@ -51,6 +51,15 @@

+ +
+ +


@@ -1120,6 +1144,17 @@ function loadPromptsList(values){ ` + `

+ __MSG_show_in__: +
+ ` + show_in_output + ` + ` + + ` +

__MSG_customPrompts_form_label_Action__:
` + action_output + `

diff --git a/pages/customprompts/mzta-custom-prompts.js b/pages/customprompts/mzta-custom-prompts.js index 5a415e11..d376beb1 100644 --- a/pages/customprompts/mzta-custom-prompts.js +++ b/pages/customprompts/mzta-custom-prompts.js @@ -1091,9 +1091,6 @@ function loadPromptsList(values){ case "both": show_in_output = `__MSG_show_in_both__`; break; - case "none": - show_in_output = `__MSG_show_in_none__`; - break; } let output = ` @@ -1151,7 +1148,6 @@ function loadPromptsList(values){ - ` + `

From 4ab2af98a3ca8ae34f217e3c8ff38ed9e327952a Mon Sep 17 00:00:00 2001 From: mic Date: Thu, 2 Apr 2026 22:12:57 +0200 Subject: [PATCH 03/37] working on new menus. see #680 --- js/mzta-utils.js | 14 ++++++- mzta-background.js | 91 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 98 insertions(+), 7 deletions(-) diff --git a/js/mzta-utils.js b/js/mzta-utils.js index 9859f7f5..79a8bad7 100644 --- a/js/mzta-utils.js +++ b/js/mzta-utils.js @@ -649,10 +649,11 @@ export function getActiveSpecialPromptsIDs(args = {}) { get_calendar_event = false, get_calendar_event_from_clipboard = false, get_task = false, + spamfilter = false, + summarize = false, + translate = false, is_chatgpt_web = false } = args; - // The Antispam filter is not here, because this method is used only - // to reload the ThunderAI button menu, not the context menu let output = []; // console.log(">>>>>>>>>> getActiveSpecialPromptsIDs args: " + JSON.stringify(args)); if (is_chatgpt_web) { @@ -673,6 +674,15 @@ export function getActiveSpecialPromptsIDs(args = {}) { if (get_task) { output.push('prompt_get_task'); } + if (spamfilter) { + output.push('prompt_spamfilter'); + } + if (summarize) { + output.push('prompt_summarize'); + } + if (translate) { + output.push('prompt_translate_this'); + } // console.log(">>>>>>>>>> getActiveSpecialPromptsIDs output: " + JSON.stringify(output)); return output; } diff --git a/mzta-background.js b/mzta-background.js index 86555154..681ed7ac 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -99,6 +99,9 @@ let special_prompts_ids = getActiveSpecialPromptsIDs({ 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), + spamfilter: prefs_init.spamfilter, + summarize: prefs_init.summarize, + translate: prefs_init.translate, is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") }); @@ -163,7 +166,7 @@ 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_calendar_event_from_clipboard: prefs_default.get_calendar_event_from_clipboard, 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, spamfilter: prefs_default.spamfilter, summarize: prefs_default.summarize, translate: prefs_default.translate}); 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); @@ -173,6 +176,9 @@ async function _reload_menus() { get_calendar_event: getCalendarEvent, get_calendar_event_from_clipboard: getCalendarEventFromClipboard, get_task: getTask, + spamfilter: prefs_reload.spamfilter, + summarize: prefs_reload.summarize, + translate: prefs_reload.translate, is_chatgpt_web: (prefs_reload.connection_type === "chatgpt_web") }); menus.reload(special_prompts_ids); @@ -1367,6 +1373,9 @@ function setupStorageChangeListener() { get_calendar_event: getCalendarEvent, get_calendar_event_from_clipboard: getCalendarEventFromClipboard, get_task: getTask, + spamfilter: prefs_init.spamfilter, + summarize: prefs_init.summarize, + translate: prefs_init.translate, is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") }); menus.reload(special_prompts_ids); @@ -1384,8 +1393,11 @@ function setupStorageChangeListener() { get_calendar_event: getCalendarEvent, get_calendar_event_from_clipboard: getCalendarEventFromClipboard, get_task: getTask, + spamfilter: prefs_init.spamfilter, + summarize: prefs_init.summarize, + translate: prefs_init.translate, is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") - }); + }); menus.reload(special_prompts_ids); } @@ -1401,8 +1413,11 @@ function setupStorageChangeListener() { get_calendar_event: getCalendarEvent, get_calendar_event_from_clipboard: getCalendarEventFromClipboard, get_task: getTask, + spamfilter: prefs_init.spamfilter, + summarize: prefs_init.summarize, + translate: prefs_init.translate, is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") - }); + }); menus.reload(special_prompts_ids); } @@ -1418,8 +1433,71 @@ function setupStorageChangeListener() { get_calendar_event: getCalendarEvent, get_calendar_event_from_clipboard: getCalendarEventFromClipboard, get_task: getTask, + spamfilter: prefs_init.spamfilter, + summarize: prefs_init.summarize, + translate: prefs_init.translate, is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") - }); + }); + menus.reload(special_prompts_ids); + } + + // Process 'spamfilter' changes + if (changes.spamfilter) { + const newSpamfilter = changes.spamfilter.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, + spamfilter: newSpamfilter, + summarize: prefs_init.summarize, + translate: prefs_init.translate, + is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") + }); + menus.reload(special_prompts_ids); + } + + // Process 'summarize' changes + if (changes.summarize) { + const newSummarize = changes.summarize.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, + spamfilter: prefs_init.spamfilter, + summarize: newSummarize, + translate: prefs_init.translate, + is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") + }); + menus.reload(special_prompts_ids); + } + + // Process 'translate' changes + if (changes.translate) { + const newTranslate = changes.translate.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, + spamfilter: prefs_init.spamfilter, + summarize: prefs_init.summarize, + translate: newTranslate, + is_chatgpt_web: (prefs_init.connection_type === "chatgpt_web") + }); menus.reload(special_prompts_ids); } @@ -1435,8 +1513,11 @@ function setupStorageChangeListener() { get_calendar_event: getCalendarEvent, get_calendar_event_from_clipboard: getCalendarEventFromClipboard, get_task: getTask, + spamfilter: prefs_init.spamfilter, + summarize: prefs_init.summarize, + translate: prefs_init.translate, is_chatgpt_web: (newConnectionType === "chatgpt_web") - }); + }); menus.reload(special_prompts_ids); } From 14868d4fd461bdb1c8f74634a588d80c19e17db5 Mon Sep 17 00:00:00 2001 From: Mic Date: Wed, 15 Apr 2026 23:25:00 +0200 Subject: [PATCH 04/37] order menu feature first try. see #680 --- _locales/en/messages.json | 92 ++++++-- js/mzta-menus.js | 6 +- js/mzta-prompts.js | 14 +- options/mzta-options.html | 1 + options/mzta-options.js | 4 + pages/menu_order/mzta-menu-order.css | 291 +++++++++++++++++++++++ pages/menu_order/mzta-menu-order.html | 39 ++++ pages/menu_order/mzta-menu-order.js | 317 ++++++++++++++++++++++++++ 8 files changed, 740 insertions(+), 24 deletions(-) create mode 100644 pages/menu_order/mzta-menu-order.css create mode 100644 pages/menu_order/mzta-menu-order.html create mode 100644 pages/menu_order/mzta-menu-order.js diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 7e05c283..0c1df6e8 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -36,11 +36,11 @@ "description": "" }, "prompt_summarize_this": { - "message": "Summarize this", + "message": "Summarize", "description": "" }, "prompt_translate_this": { - "message": "Translate this", + "message": "Translate", "description": "" }, "prompt_this": { @@ -853,7 +853,7 @@ "description": "" }, "prompt_add_tags": { - "message": "Add tags to this email", + "message": "Add tags", "description": "" }, "prompt_add_tags_full_text": { @@ -1393,7 +1393,7 @@ "description": "" }, "prompt_spamfilter" : { - "message" : "Detect spam emails", + "message" : "Analyze for spam", "description" : "" }, "prompt_spamfilter_full_text" : { @@ -1500,22 +1500,6 @@ "message": "No", "description": "" }, - "context_menu_mzta-add-tags": { - "message": "Add tags", - "description": "" - }, - "context_menu_mzta-spamfilter": { - "message": "Analyze for spam", - "description": "" - }, - "context_menu_mzta-summarize": { - "message": "Summarize", - "description": "" - }, - "context_menu_mzta-translate": { - "message": "Translate", - "description": "" - }, "noActiveCalendar": { "message": "No editable calendar found!", "description": "" @@ -2274,5 +2258,73 @@ "placeholder_string": { "message": "Placeholder", "description": "" + }, + "menu_order_title": { + "message": "Menu Order", + "description": "Title for the menu order page" + }, + "menu_order_popup_list_title": { + "message": "Popup Menu", + "description": "Header for the popup menu list in the menu order page" + }, + "menu_order_context_list_title": { + "message": "Context Menu", + "description": "Header for the context menu list in the menu order page" + }, + "menu_order_save": { + "message": "Save", + "description": "Save button label in menu order page" + }, + "menu_order_saved": { + "message": "Menu order saved.", + "description": "Message shown after saving menu order" + }, + "menu_order_tab_reading": { + "message": "Reading", + "description": "Sub-tab label for reading view ordering" + }, + "menu_order_tab_composing": { + "message": "Composing", + "description": "Sub-tab label for composing view ordering" + }, + "menu_order_badge_default": { + "message": "Default", + "description": "Badge label for default prompts" + }, + "menu_order_badge_special": { + "message": "Special", + "description": "Badge label for special prompts" + }, + "menu_order_badge_custom": { + "message": "Custom", + "description": "Badge label for custom prompts" + }, + "menu_order_type_reading": { + "message": "Reading", + "description": "Badge label for reading-only prompts" + }, + "menu_order_type_composing": { + "message": "Composing", + "description": "Badge label for composing-only prompts" + }, + "menu_order_type_always": { + "message": "Always", + "description": "Badge label for prompts shown in all contexts" + }, + "menu_order_btn_label": { + "message": "Menu Order", + "description": "Button label to open the menu order page from options" + }, + "menu_order_info": { + "message": "Drag and drop items to reorder them. Use the toggle to show or hide items in each menu.", + "description": "Info text for the menu order page" + }, + "menu_order_active_items": { + "message": "Visible items", + "description": "Section header for active/visible menu items" + }, + "menu_order_hidden_items": { + "message": "Hidden items", + "description": "Section header for hidden menu items" } } \ No newline at end of file diff --git a/js/mzta-menus.js b/js/mzta-menus.js index 1cea4993..b7230250 100644 --- a/js/mzta-menus.js +++ b/js/mzta-menus.js @@ -488,6 +488,7 @@ export class mzta_Menus { is_special: prompt.is_special, position_display: prompt.position_display, position_compose: prompt.position_compose, + position_context: prompt.position_context, }; this.shortcutMenu.push(curr_menu_entry); } @@ -514,8 +515,11 @@ export class mzta_Menus { }, resolve) ); - // Sort alphabetically for context menu + // Sort by position_context if available, otherwise alphabetically contextPrompts.sort((a, b) => { + const posA = a.position_context || 9999; + const posB = b.position_context || 9999; + if (posA !== posB) return posA - posB; const nameA = i18nConditionalGet(a.name); const nameB = i18nConditionalGet(b.name); return nameA.localeCompare(nameB); diff --git a/js/mzta-prompts.js b/js/mzta-prompts.js index 089f7184..3c924af3 100644 --- a/js/mzta-prompts.js +++ b/js/mzta-prompts.js @@ -383,7 +383,7 @@ const specialPrompts = [ api_model: '', is_default: "1", is_special: "1", - show_in: "both", + show_in: "none", }, { id: 'prompt_summarize_email_separator', @@ -400,7 +400,7 @@ const specialPrompts = [ api_model: '', is_default: "1", is_special: "1", - show_in: "both", + show_in: "none", }, { id: 'prompt_translate_this', @@ -488,7 +488,7 @@ export function preparePromptsForExport(prompts, include_api_settings = false){ } if(prompt.is_default == 1){ - let allowedKeys = ['id', 'enabled', 'position_compose', 'position_display', 'need_custom_text', 'show_in']; + let allowedKeys = ['id', 'enabled', 'position_compose', 'position_display', 'position_context', 'need_custom_text', 'show_in']; if(include_api_settings){ allowedKeys.push('api_type'); for (const [integration, options] of Object.entries(integration_options_config)) { @@ -541,6 +541,7 @@ async function getDefaultPrompts_withProps() { prompt.text = browser.i18n.getMessage(prompt.text); prompt.position_display = pos; prompt.position_compose = pos; + prompt.position_context = pos; prompt.enabled = 1; pos++; }) @@ -552,6 +553,7 @@ async function getDefaultPrompts_withProps() { if(prefs._default_prompts_properties?.[prompt.id]){ prompt.position_compose = prefs._default_prompts_properties[prompt.id].position_compose; prompt.position_display = prefs._default_prompts_properties[prompt.id].position_display; + prompt.position_context = prefs._default_prompts_properties[prompt.id]?.position_context || prompt.position_display; prompt.enabled = prefs._default_prompts_properties[prompt.id].enabled; prompt.need_custom_text = prefs._default_prompts_properties[prompt.id].need_custom_text; prompt.chatgpt_web_model = prefs._default_prompts_properties[prompt.id].chatgpt_web_model; @@ -562,6 +564,7 @@ async function getDefaultPrompts_withProps() { }else{ prompt.position_display = pos; prompt.position_compose = pos; + prompt.position_context = pos; prompt.enabled = 1; pos++; } @@ -608,6 +611,7 @@ export async function setDefaultPromptsProperties(prompts) { default_prompts_properties[prompt.id] = { position_compose: (prompt.position_compose === undefined || prompt.position_compose === "undefined") ? "" : prompt.position_compose, position_display: (prompt.position_display === undefined || prompt.position_display === "undefined") ? "" : prompt.position_display, + position_context: (prompt.position_context === undefined || prompt.position_context === "undefined") ? "" : prompt.position_context, enabled: (prompt.enabled === undefined || prompt.enabled === "undefined") ? "" : prompt.enabled, need_custom_text: (prompt.need_custom_text === undefined || prompt.need_custom_text === "undefined") ? "" : prompt.need_custom_text, chatgpt_web_model: (prompt.chatgpt_web_model === undefined || prompt.chatgpt_web_model === "undefined") ? "" : prompt.chatgpt_web_model, @@ -666,6 +670,10 @@ export async function setSpecialPrompts(prompts) { await browser.storage.local.set({_special_prompts: prompts}); } +export function getHiddenSpecialPromptIds() { + return specialPrompts.filter(p => p.show_in === "none").map(p => p.id); +} + export async function getSpamFilterPrompt(){ return (await getSpecialPrompts()).find(prompt => prompt.id == 'prompt_spamfilter'); } diff --git a/options/mzta-options.html b/options/mzta-options.html index ba3391c7..1f3d6eb4 100644 --- a/options/mzta-options.html +++ b/options/mzta-options.html @@ -271,6 +271,7 @@
__MSG_prefs_OptionText_btnManagePrompts_infoline__ __MSG_more_info_string__
+

__MSG_prefsInfoTitle__

diff --git a/options/mzta-options.js b/options/mzta-options.js index 0567e870..5f34710f 100644 --- a/options/mzta-options.js +++ b/options/mzta-options.js @@ -412,6 +412,10 @@ document.addEventListener('DOMContentLoaded', async () => { openTab('/pages/customdataplaceholders/mzta-custom-dataplaceholders.html'); }); + document.getElementById('btnMenuOrder').addEventListener('click', () => { + openTab('/pages/menu_order/mzta-menu-order.html'); + }); + document.getElementById('btnManageTagsInfo').addEventListener('click', () => { openTab('/pages/addtags/mzta-add-tags.html'); }); diff --git a/pages/menu_order/mzta-menu-order.css b/pages/menu_order/mzta-menu-order.css new file mode 100644 index 00000000..1aacf6f4 --- /dev/null +++ b/pages/menu_order/mzta-menu-order.css @@ -0,0 +1,291 @@ +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + margin: 20px; + padding: 0; +} + +.page_title { + margin-bottom: 4px; +} + +.info_text { + margin-top: 0; + font-size: 0.9em; + color: #555; +} + +#command_palette { + margin-bottom: 16px; + display: flex; + align-items: center; + gap: 12px; +} + +#btnSave { + padding: 6px 20px; + font-size: 1em; + cursor: pointer; +} + +#btnSave:disabled { + opacity: 0.5; + cursor: default; +} + +#msgDisplay { + font-size: 0.9em; + color: #2a7e2a; +} + +#lists_container { + display: flex; + gap: 24px; + align-items: flex-start; +} + +.menu_list_panel { + flex: 1; + min-width: 320px; + border: 1px solid #ccc; + border-radius: 6px; + padding: 12px; +} + +.menu_list_panel h2 { + margin-top: 0; + margin-bottom: 8px; + font-size: 1.1em; +} + +.sub_tabs { + display: flex; + gap: 4px; + margin-bottom: 12px; +} + +.sub_tab { + padding: 4px 14px; + border: 1px solid #ccc; + background: #f5f5f5; + cursor: pointer; + border-radius: 4px 4px 0 0; + font-size: 0.9em; +} + +.sub_tab.active { + background: #fff; + border-bottom-color: #fff; + font-weight: bold; +} + +.section_label { + font-size: 0.85em; + color: #666; + margin: 8px 0 4px 0; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.hidden_section_label { + border-top: 1px dashed #ccc; + padding-top: 8px; + margin-top: 12px; +} + +.sortable_list, +.hidden_list { + list-style: none; + padding: 0; + margin: 0; + min-height: 40px; +} + +.sortable_item { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 8px; + margin: 3px 0; + border: 1px solid #ddd; + border-radius: 4px; + background: #fff; + user-select: none; + transition: background-color 0.15s; +} + +.sortable_item:hover { + background: #f0f4ff; +} + +.sortable_item.dragging { + opacity: 0.4; + border-style: dashed; +} + +.sortable_item.drag-over { + border-top: 2px solid #409df3; +} + +.drag_handle { + cursor: grab; + color: #999; + font-size: 1.1em; + padding: 0 4px; + flex-shrink: 0; +} + +.drag_handle:active { + cursor: grabbing; +} + +.item_toggle { + flex-shrink: 0; + width: 16px; + height: 16px; + cursor: pointer; +} + +.item_name { + flex: 1; + font-size: 0.92em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.badge { + display: inline-block; + padding: 1px 6px; + border-radius: 3px; + font-size: 0.72em; + font-weight: 600; + text-transform: uppercase; + flex-shrink: 0; +} + +.badge_default { + background: #e0e0e0; + color: #555; +} + +.badge_special { + background: #d4eaff; + color: #1a5fa0; +} + +.badge_custom { + background: #e8f5e9; + color: #2e7d32; +} + +.badge_type { + background: #fff3e0; + color: #e65100; +} + +/* Hidden list items */ +.hidden_list .sortable_item { + opacity: 0.6; + background: #fafafa; +} + +.hidden_list .sortable_item .drag_handle { + visibility: hidden; +} + +.hidden_list .sortable_item:hover { + opacity: 0.8; + background: #f5f5f5; +} + +/* Dark mode */ +@media (prefers-color-scheme: dark) { + body { + background-color: #1c1b22; + color: rgb(251, 251, 254); + } + + .info_text { + color: #aaa; + } + + #msgDisplay { + color: #5cb85c; + } + + .menu_list_panel { + border-color: #444; + background: #2a2a30; + } + + .sub_tab { + border-color: #555; + background: #333; + color: #ccc; + } + + .sub_tab.active { + background: #2a2a30; + border-bottom-color: #2a2a30; + color: #fff; + } + + .section_label { + color: #999; + } + + .hidden_section_label { + border-top-color: #555; + } + + .sortable_item { + border-color: #444; + background: #333; + color: #eee; + } + + .sortable_item:hover { + background: #3a3a44; + } + + .sortable_item.drag-over { + border-top-color: #409df3; + } + + .drag_handle { + color: #777; + } + + .badge_default { + background: #444; + color: #bbb; + } + + .badge_special { + background: #1a3a5c; + color: #7ab8f5; + } + + .badge_custom { + background: #1b3a1e; + color: #81c784; + } + + .badge_type { + background: #3d2800; + color: #ffb74d; + } + + .hidden_list .sortable_item { + background: #2a2a2e; + } + + .hidden_list .sortable_item:hover { + background: #333; + } + + a:link { color: #409eff; } + a:visited { color: #409eff; } + a:hover { color: #66b1ff; } + a:active { color: #66b1ff; } +} diff --git a/pages/menu_order/mzta-menu-order.html b/pages/menu_order/mzta-menu-order.html new file mode 100644 index 00000000..ad5fe728 --- /dev/null +++ b/pages/menu_order/mzta-menu-order.html @@ -0,0 +1,39 @@ + + + + + ThunderAI - __MSG_menu_order_title__ + + + + +

__MSG_menu_order_title__

+

__MSG_menu_order_info__

+
+ + +
+
+ + +
+ + + + diff --git a/pages/menu_order/mzta-menu-order.js b/pages/menu_order/mzta-menu-order.js new file mode 100644 index 00000000..3335fa0e --- /dev/null +++ b/pages/menu_order/mzta-menu-order.js @@ -0,0 +1,317 @@ +/* + * ThunderAI [https://micz.it/thunderbird-addon-thunderai/] + * Copyright (C) 2024 - 2026 Mic (m@micz.it) + + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import { getPrompts, setDefaultPromptsProperties, setCustomPrompts, setSpecialPrompts, getHiddenSpecialPromptIds } from '../../js/mzta-prompts.js'; +import { i18nConditionalGet } from '../../js/mzta-utils.js'; + +let allPrompts = []; +let currentPopupView = 'display'; // 'display' or 'compose' +let hasUnsavedChanges = false; + +document.addEventListener('DOMContentLoaded', async () => { + allPrompts = await getPrompts(false, [], true); + + // Exclude special prompts that are defined with show_in: "none" (internal prompts, not user-toggleable) + const hiddenSpecialIds = getHiddenSpecialPromptIds(); + allPrompts = allPrompts.filter(p => !hiddenSpecialIds.includes(p.id)); + + // Resolve i18n names and assign initial position_context if missing + let contextPos = 1; + const sortedForContext = [...allPrompts].sort((a, b) => { + const nameA = i18nConditionalGet(a.name); + const nameB = i18nConditionalGet(b.name); + return nameA.localeCompare(nameB); + }); + sortedForContext.forEach(p => { + if (p.position_context === undefined || p.position_context === '' || p.position_context === 'undefined') { + p.position_context = contextPos; + } + contextPos++; + }); + + // Resolve display names + allPrompts.forEach(p => { + p._displayName = i18nConditionalGet(p.name); + }); + + renderPopupList(); + renderContextList(); + initSubTabs(); + + document.getElementById('btnSave').addEventListener('click', saveAll); + + i18n.updateDocument(); +}); + +// ==================== Sub-tabs ==================== + +function initSubTabs() { + document.querySelectorAll('.sub_tab').forEach(btn => { + btn.addEventListener('click', () => { + document.querySelectorAll('.sub_tab').forEach(b => b.classList.remove('active')); + btn.classList.add('active'); + currentPopupView = btn.dataset.view; + renderPopupList(); + }); + }); +} + +// ==================== Render Popup List ==================== + +function renderPopupList() { + const posKey = currentPopupView === 'display' ? 'position_display' : 'position_compose'; + + const activeItems = allPrompts.filter(p => { + const showIn = p.show_in || 'popup'; + return showIn === 'popup' || showIn === 'both'; + }); + const hiddenItems = allPrompts.filter(p => { + const showIn = p.show_in || 'popup'; + return showIn !== 'popup' && showIn !== 'both'; + }); + + activeItems.sort((a, b) => (a[posKey] || 9999) - (b[posKey] || 9999)); + hiddenItems.sort((a, b) => a._displayName.localeCompare(b._displayName)); + + const activeList = document.getElementById('popup_list'); + const hiddenList = document.getElementById('popup_list_hidden'); + + renderListItems(activeList, activeItems, 'popup', true); + renderListItems(hiddenList, hiddenItems, 'popup', false); + + initDragAndDrop(activeList, posKey); +} + +// ==================== Render Context List ==================== + +function renderContextList() { + // Only show items with type 0 or 1 (not composing-only) + const contextEligible = allPrompts.filter(p => String(p.type) === '0' || String(p.type) === '1'); + + const activeItems = contextEligible.filter(p => { + const showIn = p.show_in || 'popup'; + return showIn === 'context' || showIn === 'both'; + }); + const hiddenItems = contextEligible.filter(p => { + const showIn = p.show_in || 'popup'; + return showIn !== 'context' && showIn !== 'both'; + }); + + activeItems.sort((a, b) => (a.position_context || 9999) - (b.position_context || 9999)); + hiddenItems.sort((a, b) => a._displayName.localeCompare(b._displayName)); + + const activeList = document.getElementById('context_list'); + const hiddenList = document.getElementById('context_list_hidden'); + + renderListItems(activeList, activeItems, 'context', true); + renderListItems(hiddenList, hiddenItems, 'context', false); + + initDragAndDrop(activeList, 'position_context'); +} + +// ==================== Render List Items ==================== + +function renderListItems(listEl, items, menuType, isActive) { + listEl.innerHTML = ''; + items.forEach(prompt => { + const li = document.createElement('li'); + li.classList.add('sortable_item'); + li.dataset.id = prompt.id; + if (isActive) { + li.draggable = true; + } + + // Drag handle + const handle = document.createElement('span'); + handle.classList.add('drag_handle'); + handle.textContent = '\u2630'; + li.appendChild(handle); + + // Toggle checkbox + const toggle = document.createElement('input'); + toggle.type = 'checkbox'; + toggle.classList.add('item_toggle'); + toggle.checked = isActive; + toggle.addEventListener('change', () => { + toggleShowIn(prompt, menuType, toggle.checked); + }); + li.appendChild(toggle); + + // Name + const nameSpan = document.createElement('span'); + nameSpan.classList.add('item_name'); + nameSpan.textContent = prompt._displayName; + li.appendChild(nameSpan); + + // Type badge + const typeBadge = document.createElement('span'); + typeBadge.classList.add('badge', 'badge_type'); + if (String(prompt.type) === '1') { + typeBadge.textContent = browser.i18n.getMessage('menu_order_type_reading'); + } else if (String(prompt.type) === '2') { + typeBadge.textContent = browser.i18n.getMessage('menu_order_type_composing'); + } else { + typeBadge.textContent = browser.i18n.getMessage('menu_order_type_always'); + } + li.appendChild(typeBadge); + + // Source badge + const sourceBadge = document.createElement('span'); + sourceBadge.classList.add('badge'); + if (String(prompt.is_special) === '1') { + sourceBadge.classList.add('badge_special'); + sourceBadge.textContent = browser.i18n.getMessage('menu_order_badge_special'); + } else if (String(prompt.is_default) === '1') { + sourceBadge.classList.add('badge_default'); + sourceBadge.textContent = browser.i18n.getMessage('menu_order_badge_default'); + } else { + sourceBadge.classList.add('badge_custom'); + sourceBadge.textContent = browser.i18n.getMessage('menu_order_badge_custom'); + } + li.appendChild(sourceBadge); + + listEl.appendChild(li); + }); +} + +// ==================== Toggle show_in ==================== + +function toggleShowIn(prompt, menuType, isOn) { + const current = prompt.show_in || 'popup'; + + if (menuType === 'popup') { + if (isOn) { + prompt.show_in = (current === 'none') ? 'popup' : (current === 'context') ? 'both' : current; + } else { + prompt.show_in = (current === 'popup') ? 'none' : (current === 'both') ? 'context' : current; + } + } else { // context + if (isOn) { + prompt.show_in = (current === 'none') ? 'context' : (current === 'popup') ? 'both' : current; + } else { + prompt.show_in = (current === 'context') ? 'none' : (current === 'both') ? 'popup' : current; + } + } + + markUnsaved(); + renderPopupList(); + renderContextList(); +} + +// ==================== Drag and Drop ==================== + +function initDragAndDrop(listEl, positionKey) { + let draggedItem = null; + + listEl.addEventListener('dragstart', (e) => { + const li = e.target.closest('.sortable_item'); + if (!li) return; + draggedItem = li; + draggedItem.classList.add('dragging'); + e.dataTransfer.effectAllowed = 'move'; + e.dataTransfer.setData('text/plain', li.dataset.id); + }); + + listEl.addEventListener('dragover', (e) => { + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; + if (!draggedItem) return; + + // Remove previous drag-over indicators + listEl.querySelectorAll('.drag-over').forEach(el => el.classList.remove('drag-over')); + + const afterElement = getDragAfterElement(listEl, e.clientY); + if (afterElement) { + afterElement.classList.add('drag-over'); + listEl.insertBefore(draggedItem, afterElement); + } else { + listEl.appendChild(draggedItem); + } + }); + + listEl.addEventListener('dragleave', (e) => { + if (e.target.classList) { + e.target.classList.remove('drag-over'); + } + }); + + listEl.addEventListener('drop', (e) => { + e.preventDefault(); + listEl.querySelectorAll('.drag-over').forEach(el => el.classList.remove('drag-over')); + }); + + listEl.addEventListener('dragend', () => { + if (draggedItem) { + draggedItem.classList.remove('dragging'); + updatePositionsFromDOM(listEl, positionKey); + draggedItem = null; + markUnsaved(); + } + }); +} + +function getDragAfterElement(container, y) { + const draggableElements = [...container.querySelectorAll('.sortable_item:not(.dragging)')]; + return draggableElements.reduce((closest, child) => { + const box = child.getBoundingClientRect(); + const offset = y - box.top - box.height / 2; + if (offset < 0 && offset > closest.offset) { + return { offset: offset, element: child }; + } + return closest; + }, { offset: Number.NEGATIVE_INFINITY }).element; +} + +function updatePositionsFromDOM(listEl, positionKey) { + const items = listEl.querySelectorAll('.sortable_item'); + items.forEach((li, index) => { + const promptId = li.dataset.id; + const prompt = allPrompts.find(p => p.id === promptId); + if (prompt) { + prompt[positionKey] = index + 1; + } + }); +} + +// ==================== Save ==================== + +async function saveAll() { + const btnSave = document.getElementById('btnSave'); + const msgDisplay = document.getElementById('msgDisplay'); + btnSave.disabled = true; + + const defaultPromptsToSave = allPrompts.filter(p => String(p.is_default) === '1' && String(p.is_special) !== '1'); + const customPromptsToSave = allPrompts.filter(p => String(p.is_default) === '0' && String(p.is_special) !== '1'); + const specialPromptsToSave = allPrompts.filter(p => String(p.is_special) === '1'); + + await setDefaultPromptsProperties(defaultPromptsToSave); + await setCustomPrompts(customPromptsToSave); + await setSpecialPrompts(specialPromptsToSave); + + await browser.runtime.sendMessage({ command: "reload_menus" }); + + hasUnsavedChanges = false; + msgDisplay.textContent = browser.i18n.getMessage('menu_order_saved'); + setTimeout(() => { msgDisplay.textContent = ''; }, 3000); +} + +function markUnsaved() { + hasUnsavedChanges = true; + document.getElementById('btnSave').disabled = false; + document.getElementById('msgDisplay').textContent = ''; +} From e89ab5becae44da86c4dec97f6982dc5126e9571 Mon Sep 17 00:00:00 2001 From: Mic Date: Wed, 15 Apr 2026 23:31:00 +0200 Subject: [PATCH 05/37] inactive features items hidded. see #680 --- mzta-background.js | 21 +++++++++++++++++++++ pages/menu_order/mzta-menu-order.js | 11 +++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/mzta-background.js b/mzta-background.js index 681ed7ac..4c0832a3 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -186,6 +186,24 @@ async function _reload_menus() { return true; } +async function _getActiveSpecialIds() { + 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, spamfilter: prefs_default.spamfilter, summarize: prefs_default.summarize, translate: prefs_default.translate}); + 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); + return 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, + spamfilter: prefs_reload.spamfilter, + summarize: prefs_reload.summarize, + translate: prefs_reload.translate, + is_chatgpt_web: (prefs_reload.connection_type === "chatgpt_web") + }); +} + async function _assign_tags(_data, create_new_tags = true, exclusions_exact_match = false) { let all_tags_list = await getTagsList(); all_tags_list = all_tags_list[1]; @@ -506,6 +524,9 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => { case 'reload_menus': return _reload_menus(); break; + case 'get_active_special_ids': + return _getActiveSpecialIds(); + break; case 'shortcut_do_prompt': taLog.log("Executing shortcut, promptId: " + message.promptId); return menus.executeMenuAction(message.promptId); diff --git a/pages/menu_order/mzta-menu-order.js b/pages/menu_order/mzta-menu-order.js index 3335fa0e..55a81785 100644 --- a/pages/menu_order/mzta-menu-order.js +++ b/pages/menu_order/mzta-menu-order.js @@ -20,6 +20,7 @@ import { getPrompts, setDefaultPromptsProperties, setCustomPrompts, setSpecialPr import { i18nConditionalGet } from '../../js/mzta-utils.js'; let allPrompts = []; +let allExcludedSpecialPrompts = []; // special prompts excluded from UI (hidden + inactive features), preserved on save let currentPopupView = 'display'; // 'display' or 'compose' let hasUnsavedChanges = false; @@ -27,8 +28,14 @@ document.addEventListener('DOMContentLoaded', async () => { allPrompts = await getPrompts(false, [], true); // Exclude special prompts that are defined with show_in: "none" (internal prompts, not user-toggleable) + // and special prompts whose feature is not active (e.g. add_tags disabled, sparks not present) const hiddenSpecialIds = getHiddenSpecialPromptIds(); - allPrompts = allPrompts.filter(p => !hiddenSpecialIds.includes(p.id)); + const activeSpecialIds = await browser.runtime.sendMessage({ command: "get_active_special_ids" }); + allExcludedSpecialPrompts = allPrompts.filter(p => + hiddenSpecialIds.includes(p.id) || + (String(p.is_special) === '1' && !activeSpecialIds.includes(p.id)) + ); + allPrompts = allPrompts.filter(p => !allExcludedSpecialPrompts.some(e => e.id === p.id)); // Resolve i18n names and assign initial position_context if missing let contextPos = 1; @@ -297,7 +304,7 @@ async function saveAll() { const defaultPromptsToSave = allPrompts.filter(p => String(p.is_default) === '1' && String(p.is_special) !== '1'); const customPromptsToSave = allPrompts.filter(p => String(p.is_default) === '0' && String(p.is_special) !== '1'); - const specialPromptsToSave = allPrompts.filter(p => String(p.is_special) === '1'); + const specialPromptsToSave = allPrompts.filter(p => String(p.is_special) === '1').concat(allExcludedSpecialPrompts); await setDefaultPromptsProperties(defaultPromptsToSave); await setCustomPrompts(customPromptsToSave); From fa54572279faa52aae55a695614c77d5ed1938bf Mon Sep 17 00:00:00 2001 From: Mic Date: Wed, 15 Apr 2026 23:39:00 +0200 Subject: [PATCH 06/37] tab filtering fixed. see #680 --- pages/menu_order/mzta-menu-order.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pages/menu_order/mzta-menu-order.js b/pages/menu_order/mzta-menu-order.js index 55a81785..743d892b 100644 --- a/pages/menu_order/mzta-menu-order.js +++ b/pages/menu_order/mzta-menu-order.js @@ -82,12 +82,15 @@ function initSubTabs() { function renderPopupList() { const posKey = currentPopupView === 'display' ? 'position_display' : 'position_compose'; + // Filter by type: reading view shows type 0+1, composing view shows type 0+2 + const allowedTypes = currentPopupView === 'display' ? ['0', '1'] : ['0', '2']; + const typeFiltered = allPrompts.filter(p => allowedTypes.includes(String(p.type))); - const activeItems = allPrompts.filter(p => { + const activeItems = typeFiltered.filter(p => { const showIn = p.show_in || 'popup'; return showIn === 'popup' || showIn === 'both'; }); - const hiddenItems = allPrompts.filter(p => { + const hiddenItems = typeFiltered.filter(p => { const showIn = p.show_in || 'popup'; return showIn !== 'popup' && showIn !== 'both'; }); From 156a0fa9a87988435d2fe02a695386deaec21066 Mon Sep 17 00:00:00 2001 From: Mic Date: Thu, 16 Apr 2026 21:41:56 +0200 Subject: [PATCH 07/37] save button improved. see #680 --- pages/menu_order/mzta-menu-order.css | 22 +++++++++++----------- pages/menu_order/mzta-menu-order.html | 2 +- pages/menu_order/mzta-menu-order.js | 20 ++++++++++++++------ 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/pages/menu_order/mzta-menu-order.css b/pages/menu_order/mzta-menu-order.css index 1aacf6f4..d09aa5cb 100644 --- a/pages/menu_order/mzta-menu-order.css +++ b/pages/menu_order/mzta-menu-order.css @@ -21,20 +21,12 @@ body { gap: 12px; } -#btnSave { - padding: 6px 20px; - font-size: 1em; - cursor: pointer; -} - -#btnSave:disabled { - opacity: 0.5; - cursor: default; +#btnSaveAll { + float: left; } #msgDisplay { - font-size: 0.9em; - color: #2a7e2a; + padding-left: 40px; } #lists_container { @@ -61,6 +53,9 @@ body { display: flex; gap: 4px; margin-bottom: 12px; + border-bottom: 1px solid #ccc; + padding-bottom: 0; + padding-left: 12px; } .sub_tab { @@ -70,6 +65,7 @@ body { cursor: pointer; border-radius: 4px 4px 0 0; font-size: 0.9em; + margin-bottom: -1px; } .sub_tab.active { @@ -218,6 +214,10 @@ body { background: #2a2a30; } + .sub_tabs { + border-bottom-color: #555; + } + .sub_tab { border-color: #555; background: #333; diff --git a/pages/menu_order/mzta-menu-order.html b/pages/menu_order/mzta-menu-order.html index ad5fe728..6635951c 100644 --- a/pages/menu_order/mzta-menu-order.html +++ b/pages/menu_order/mzta-menu-order.html @@ -10,7 +10,7 @@

__MSG_menu_order_title__

__MSG_menu_order_info__

- +
diff --git a/pages/menu_order/mzta-menu-order.js b/pages/menu_order/mzta-menu-order.js index 743d892b..b3d74027 100644 --- a/pages/menu_order/mzta-menu-order.js +++ b/pages/menu_order/mzta-menu-order.js @@ -60,7 +60,7 @@ document.addEventListener('DOMContentLoaded', async () => { renderContextList(); initSubTabs(); - document.getElementById('btnSave').addEventListener('click', saveAll); + document.getElementById('btnSaveAll').addEventListener('click', saveAll); i18n.updateDocument(); }); @@ -301,9 +301,9 @@ function updatePositionsFromDOM(listEl, positionKey) { // ==================== Save ==================== async function saveAll() { - const btnSave = document.getElementById('btnSave'); + const btnSaveAll = document.getElementById('btnSaveAll'); const msgDisplay = document.getElementById('msgDisplay'); - btnSave.disabled = true; + btnSaveAll.disabled = true; const defaultPromptsToSave = allPrompts.filter(p => String(p.is_default) === '1' && String(p.is_special) !== '1'); const customPromptsToSave = allPrompts.filter(p => String(p.is_default) === '0' && String(p.is_special) !== '1'); @@ -317,11 +317,19 @@ async function saveAll() { hasUnsavedChanges = false; msgDisplay.textContent = browser.i18n.getMessage('menu_order_saved'); - setTimeout(() => { msgDisplay.textContent = ''; }, 3000); + msgDisplay.style.display = 'inline'; + msgDisplay.style.color = 'green'; + setTimeout(() => { + msgDisplay.textContent = ''; + msgDisplay.style.display = 'none'; + }, 3000); } function markUnsaved() { hasUnsavedChanges = true; - document.getElementById('btnSave').disabled = false; - document.getElementById('msgDisplay').textContent = ''; + document.getElementById('btnSaveAll').disabled = false; + const msgDisplay = document.getElementById('msgDisplay'); + msgDisplay.textContent = browser.i18n.getMessage('customPrompts_unsaved_changes'); + msgDisplay.style.display = 'inline'; + msgDisplay.style.color = 'red'; } From a402f378f082c90ee58d6f86fe75b42ef7563f95 Mon Sep 17 00:00:00 2001 From: Mic Date: Thu, 16 Apr 2026 22:09:13 +0200 Subject: [PATCH 08/37] title css fixed. see #680 --- pages/menu_order/mzta-menu-order.css | 18 +----------------- pages/menu_order/mzta-menu-order.html | 2 +- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/pages/menu_order/mzta-menu-order.css b/pages/menu_order/mzta-menu-order.css index d09aa5cb..a2f5905e 100644 --- a/pages/menu_order/mzta-menu-order.css +++ b/pages/menu_order/mzta-menu-order.css @@ -1,17 +1,5 @@ -body { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; - margin: 20px; - padding: 0; -} - .page_title { - margin-bottom: 4px; -} - -.info_text { - margin-top: 0; - font-size: 0.9em; - color: #555; + margin-bottom: 0px; } #command_palette { @@ -201,10 +189,6 @@ body { color: rgb(251, 251, 254); } - .info_text { - color: #aaa; - } - #msgDisplay { color: #5cb85c; } diff --git a/pages/menu_order/mzta-menu-order.html b/pages/menu_order/mzta-menu-order.html index 6635951c..1370c182 100644 --- a/pages/menu_order/mzta-menu-order.html +++ b/pages/menu_order/mzta-menu-order.html @@ -8,7 +8,7 @@

__MSG_menu_order_title__

-

__MSG_menu_order_info__

+

__MSG_menu_order_info__

From 8522a28a7dc52594cb8514b0f822de7d4abbadc3 Mon Sep 17 00:00:00 2001 From: Mic Date: Thu, 16 Apr 2026 22:11:40 +0200 Subject: [PATCH 09/37] no more menu items alphabetical order. see #680 --- popup/mzta-popup.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/popup/mzta-popup.js b/popup/mzta-popup.js index 4a56f41c..a8e1bee1 100644 --- a/popup/mzta-popup.js +++ b/popup/mzta-popup.js @@ -82,15 +82,9 @@ document.addEventListener('DOMContentLoaded', async () => { async function searchPrompt(allPrompts, tabId, tabType, filtering){ taLog.log("tabType: " + tabType); - let prefs_order = await browser.storage.sync.get({dynamic_menu_order_alphabet: true}); - - if(prefs_order.dynamic_menu_order_alphabet){ - allPrompts.sort((a, b) => a.label.localeCompare(b.label)); - } else { - // Sort by position: use position_display for reading (filtering=1), position_compose for composing (filtering=2) - const posKey = filtering === 2 ? 'position_compose' : 'position_display'; - allPrompts.sort((a, b) => (a[posKey] || 9999) - (b[posKey] || 9999)); - } + // Sort by position: use position_display for reading (filtering=1), position_compose for composing (filtering=2) + const posKey = filtering === 2 ? 'position_compose' : 'position_display'; + allPrompts.sort((a, b) => (a[posKey] || 9999) - (b[posKey] || 9999)); // console.log(">>>>>>>>> allPrompts: " + JSON.stringify(allPrompts)); From a98e2edafea4c201027c81b684ce7b7f235617b3 Mon Sep 17 00:00:00 2001 From: Mic Date: Thu, 16 Apr 2026 22:18:24 +0200 Subject: [PATCH 10/37] migrateMenuOrderAlphabetic implemented. see #680 --- js/mzta-prompts.js | 46 ++++++++++++++++++++++++++++++++++++++++++++++ mzta-background.js | 4 +++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/js/mzta-prompts.js b/js/mzta-prompts.js index 3c924af3..13c76b83 100644 --- a/js/mzta-prompts.js +++ b/js/mzta-prompts.js @@ -674,6 +674,52 @@ export function getHiddenSpecialPromptIds() { return specialPrompts.filter(p => p.show_in === "none").map(p => p.id); } +// Migration: if dynamic_menu_order_alphabet was true (or unset), assign initial positions +// so that prompts appear alphabetically with special prompts first, then disable the flag +// to switch to position-based ordering permanently. +export async function migrateMenuOrderAlphabetic() { + const prefs = await browser.storage.sync.get({ dynamic_menu_order_alphabet: true }); + if (!prefs.dynamic_menu_order_alphabet) { + return; + } + + const allPrompts = await getPrompts(false, [], true); + const hiddenSpecialIds = getHiddenSpecialPromptIds(); + const visiblePrompts = allPrompts.filter(p => !hiddenSpecialIds.includes(p.id)); + + const resolveName = (p) => { + const n = p.name || ''; + if (n.startsWith('__MSG_') && n.endsWith('__')) { + return browser.i18n.getMessage(n.substring(6, n.length - 2)); + } + return n; + }; + + const specials = visiblePrompts.filter(p => String(p.is_special) === '1') + .sort((a, b) => resolveName(a).localeCompare(resolveName(b))); + const others = visiblePrompts.filter(p => String(p.is_special) !== '1') + .sort((a, b) => resolveName(a).localeCompare(resolveName(b))); + const ordered = specials.concat(others); + + ordered.forEach((prompt, idx) => { + const pos = idx + 1; + prompt.position_display = pos; + prompt.position_compose = pos; + prompt.position_context = pos; + }); + + const defaultPromptsToSave = ordered.filter(p => String(p.is_default) === '1' && String(p.is_special) !== '1'); + const customPromptsToSave = ordered.filter(p => String(p.is_default) === '0' && String(p.is_special) !== '1'); + const visibleSpecialsToSave = ordered.filter(p => String(p.is_special) === '1'); + const hiddenSpecialsToPreserve = allPrompts.filter(p => hiddenSpecialIds.includes(p.id)); + + await setDefaultPromptsProperties(defaultPromptsToSave); + await setCustomPrompts(customPromptsToSave); + await setSpecialPrompts(visibleSpecialsToSave.concat(hiddenSpecialsToPreserve)); + + await browser.storage.sync.set({ dynamic_menu_order_alphabet: false }); +} + export async function getSpamFilterPrompt(){ return (await getSpecialPrompts()).find(prompt => prompt.id == 'prompt_spamfilter'); } diff --git a/mzta-background.js b/mzta-background.js index 4c0832a3..646a0e1c 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -53,7 +53,8 @@ import { import { taPromptUtils } from './js/mzta-utils-prompt.js'; import { mzta_specialCommand } from './js/mzta-special-commands.js'; import { - getSpamFilterPrompt + getSpamFilterPrompt, + migrateMenuOrderAlphabetic } from './js/mzta-prompts.js'; import { taSpamReport } from './js/mzta-spamreport.js'; import { taSummaryStore } from './js/mzta-summarystore.js'; @@ -1571,6 +1572,7 @@ function setupPermissionsRemovedListener() { setupPermissionsRemovedListener(); // Menus handling +await migrateMenuOrderAlphabetic(); const menus = new mzta_Menus(openChatGPT, prefs_init.do_debug); menus.loadMenus(special_prompts_ids); From 1984a70b289d6d907879dc98d22ab0d29df4320a Mon Sep 17 00:00:00 2001 From: Mic Date: Thu, 16 Apr 2026 22:20:37 +0200 Subject: [PATCH 11/37] dynamic_menu_order_alphabet option removed. see #680 --- _locales/en/messages.json | 8 -------- claude-spec/05-options.md | 1 - options/mzta-options-default.js | 1 - options/mzta-options.html | 11 ----------- 4 files changed, 21 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 0c1df6e8..ced42082 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -628,14 +628,6 @@ "message": "If checked, using the keyboard shortcut CTRL+ALT+A will automatically send the highlighted prompt from the menu. Otherwise, the prompt name will be displayed to the user, requiring another press of the Enter key to send it.", "description": "" }, - "prefs_OptionText_dynamic_menu_order_alphabet": { - "message": "Menu: order alphabetically", - "description": "" - }, - "prefs_OptionText_dynamic_menu_order_alphabet_info": { - "message": "If checked, the prompts in the menu will be ordered alphabetically.", - "description": "" - }, "prefs_OptionText_chatgpt_win_dims_info": { "message": "Set to 0 if you don't want to specify the window size.", "description": "" diff --git a/claude-spec/05-options.md b/claude-spec/05-options.md index d8d14ef3..db0fad58 100644 --- a/claude-spec/05-options.md +++ b/claude-spec/05-options.md @@ -64,7 +64,6 @@ These are generated programmatically at the bottom of `mzta-options-default.js` | `chatgpt_web_custom_gpt` | `''` | Custom GPT URL | | `chatgpt_web_load_wait_time` | `1000` | Wait time (ms) for ChatGPT page | | `dynamic_menu_force_enter` | `false` | Force Enter to submit in popup | -| `dynamic_menu_order_alphabet` | `true` | Sort prompts alphabetically | | `placeholders_use_default_value` | `false` | Use placeholder defaults when empty | | `max_prompt_length` | `30000` | Max prompt string length | diff --git a/options/mzta-options-default.js b/options/mzta-options-default.js index e1f2eddb..a8c5c0db 100644 --- a/options/mzta-options-default.js +++ b/options/mzta-options-default.js @@ -114,7 +114,6 @@ export const prefs_default = { chatgpt_web_custom_gpt: '', chatgpt_web_load_wait_time: 1000, dynamic_menu_force_enter: false, - dynamic_menu_order_alphabet: true, placeholders_use_default_value: false, max_prompt_length: 30000, // max string length for prompt add_tags: false, diff --git a/options/mzta-options.html b/options/mzta-options.html index 1f3d6eb4..6d22c365 100644 --- a/options/mzta-options.html +++ b/options/mzta-options.html @@ -115,17 +115,6 @@ - - - - - - + + + + + +

__MSG_prefsInfoTitle__

From dd1aba576d50e0ab51ce3ecb0863a345f2eed2d7 Mon Sep 17 00:00:00 2001 From: Mic Date: Thu, 16 Apr 2026 22:52:29 +0200 Subject: [PATCH 17/37] reloading menu order page on prompts save. see #680 --- pages/menu_order/mzta-menu-order.js | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/pages/menu_order/mzta-menu-order.js b/pages/menu_order/mzta-menu-order.js index 9669cfed..c984ce83 100644 --- a/pages/menu_order/mzta-menu-order.js +++ b/pages/menu_order/mzta-menu-order.js @@ -26,6 +26,28 @@ let currentPopupView = 'display'; // 'display' or 'compose' let hasUnsavedChanges = false; document.addEventListener('DOMContentLoaded', async () => { + await loadAndRender(); + initSubTabs(); + + document.getElementById('btnSaveAll').addEventListener('click', saveAll); + + // If prompts are modified elsewhere (e.g. custom prompts page saving), reload this page's data. + // We skip reload if we have unsaved changes, to avoid losing user work. + let reloadDebounce = null; + browser.storage.onChanged.addListener((changes, areaName) => { + if (areaName !== 'local') return; + if (!(changes._default_prompts_properties || changes._custom_prompt || changes._special_prompts)) return; + if (hasUnsavedChanges) return; + clearTimeout(reloadDebounce); + reloadDebounce = setTimeout(() => { + loadAndRender(); + }, 200); + }); + + i18n.updateDocument(); +}); + +async function loadAndRender() { allPrompts = await getPrompts(false, [], true); // Exclude special prompts that are defined with show_in: "none" (internal prompts, not user-toggleable) @@ -63,12 +85,7 @@ document.addEventListener('DOMContentLoaded', async () => { renderPopupList(); renderContextList(); - initSubTabs(); - - document.getElementById('btnSaveAll').addEventListener('click', saveAll); - - i18n.updateDocument(); -}); +} // ==================== Sub-tabs ==================== From 3a506f710f071864c3c083d0f980e7b07deb1033 Mon Sep 17 00:00:00 2001 From: Mic Date: Thu, 16 Apr 2026 22:54:52 +0200 Subject: [PATCH 18/37] menu order reloading fixed. see #680 --- pages/menu_order/mzta-menu-order.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pages/menu_order/mzta-menu-order.js b/pages/menu_order/mzta-menu-order.js index c984ce83..f49e2c94 100644 --- a/pages/menu_order/mzta-menu-order.js +++ b/pages/menu_order/mzta-menu-order.js @@ -23,7 +23,6 @@ let allPrompts = []; let allExcludedSpecialPrompts = []; // special prompts excluded from UI (hidden + inactive features), preserved on save let allDisabledPrompts = []; // default/custom prompts disabled (enabled=0), excluded from UI, preserved on save let currentPopupView = 'display'; // 'display' or 'compose' -let hasUnsavedChanges = false; document.addEventListener('DOMContentLoaded', async () => { await loadAndRender(); @@ -32,14 +31,17 @@ document.addEventListener('DOMContentLoaded', async () => { document.getElementById('btnSaveAll').addEventListener('click', saveAll); // If prompts are modified elsewhere (e.g. custom prompts page saving), reload this page's data. - // We skip reload if we have unsaved changes, to avoid losing user work. + // Any unsaved changes on this page are discarded to avoid overwriting the other page's changes. let reloadDebounce = null; browser.storage.onChanged.addListener((changes, areaName) => { if (areaName !== 'local') return; if (!(changes._default_prompts_properties || changes._custom_prompt || changes._special_prompts)) return; - if (hasUnsavedChanges) return; clearTimeout(reloadDebounce); reloadDebounce = setTimeout(() => { + document.getElementById('btnSaveAll').disabled = true; + const msgDisplay = document.getElementById('msgDisplay'); + msgDisplay.textContent = ''; + msgDisplay.style.display = 'none'; loadAndRender(); }, 200); }); @@ -341,7 +343,6 @@ async function saveAll() { await browser.runtime.sendMessage({ command: "reload_menus" }); - hasUnsavedChanges = false; msgDisplay.textContent = browser.i18n.getMessage('menu_order_saved'); msgDisplay.style.display = 'inline'; msgDisplay.style.color = 'green'; @@ -352,7 +353,6 @@ async function saveAll() { } function markUnsaved() { - hasUnsavedChanges = true; document.getElementById('btnSaveAll').disabled = false; const msgDisplay = document.getElementById('msgDisplay'); msgDisplay.textContent = browser.i18n.getMessage('customPrompts_unsaved_changes'); From 4575ab795db5f9d8da1be25cf816cfe663b0080c Mon Sep 17 00:00:00 2001 From: Mic Date: Thu, 16 Apr 2026 22:59:21 +0200 Subject: [PATCH 19/37] 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 73d65735..0baa27ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@
  • Antispam information are now permanently saved for each message [#675].
  • [All APIs] A summary has been added above the mail content [#580].
  • [All APIs] Inline auto translation for emails added [#247].
  • +
  • Custom menus configuration added. Now it's possibile to define which prompts show in the ThunderAI menu, which ones in the context menu and in which order [#680].
  • ...
  • Version 4.0.3 - 20/03/2026

    diff --git a/options/mzta-release-notes.html b/options/mzta-release-notes.html index 7a4c2276..a204a653 100644 --- a/options/mzta-release-notes.html +++ b/options/mzta-release-notes.html @@ -12,6 +12,7 @@
  • Antispam information are now permanently saved for each message [#675].
  • [All APIs] A summary has been added above the mail content [#580].
  • [All APIs] Inline auto translation for emails added [#247].
  • +
  • Custom menus configuration added. Now it's possibile to define which prompts show in the ThunderAI menu, which ones in the context menu and in which order [#680].
  • ...
  • Version 4.0.3 - 20/03/2026

    From e4c065df7b9dc5da18f8f9c4cb118d8f14b362b7 Mon Sep 17 00:00:00 2001 From: Mic Date: Thu, 16 Apr 2026 23:00:07 +0200 Subject: [PATCH 20/37] spec files updated. see #680 --- claude-spec/01-architecture.md | 1 + claude-spec/02-prompts.md | 61 +++++++++++++++++++++++++++++++--- claude-spec/05-options.md | 5 +++ 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/claude-spec/01-architecture.md b/claude-spec/01-architecture.md index 13209150..65dd6ae7 100644 --- a/claude-spec/01-architecture.md +++ b/claude-spec/01-architecture.md @@ -210,6 +210,7 @@ Each subdirectory is a self-contained settings/UI page for a specific feature: | `customdataplaceholders/` | Custom placeholder editor | | `get-calendar-event/` | Calendar event extraction settings | | `get-task/` | Task creation settings | +| `menu_order/` | Drag-and-drop reordering and visibility control for popup and context menus | | `spamfilter/` | Spam filter settings | | `summarize/` | Email summarization settings | | `translate/` | Email translation settings | diff --git a/claude-spec/02-prompts.md b/claude-spec/02-prompts.md index 5925d229..ae33e52e 100644 --- a/claude-spec/02-prompts.md +++ b/claude-spec/02-prompts.md @@ -29,9 +29,10 @@ Prompts are the core user-facing feature of ThunderAI. Each prompt defines an AI | Property | Type | Description | |----------|------|-------------| | `enabled` | number | `0` = hidden, `1` = shown in menus | -| `position_display` | number | Sort order in reading view (used when alphabetical ordering is off) | -| `position_compose` | number | Sort order in compose view (used when alphabetical ordering is off) | -| `show_in` | string | `"popup"` = popup only, `"context"` = context menu only, `"both"` = both. Default: `"popup"` for default/custom prompts, `"both"` for special prompts | +| `position_display` | number | Sort order for the popup menu in reading view | +| `position_compose` | number | Sort order for the popup menu in compose view | +| `position_context` | number | Sort order for the context menu | +| `show_in` | string | `"popup"` = popup only, `"context"` = context menu only, `"both"` = both, `"none"` = hidden from all menus. Default: `"popup"` for default/custom prompts, `"both"` for special prompts | ### Per-Prompt API Override Properties @@ -63,17 +64,67 @@ These special prompts can have their own dedicated API integration settings (con ## Menu System ### Popup Menu -- Displays prompts filtered by `show_in` (`"popup"` or `"both"`) and by tab context (`type` property) -- Ordering: user can choose between alphabetical or position-based (using `position_display`/`position_compose`) via the `dynamic_menu_order_alphabet` preference +- Displays prompts filtered by `show_in` (`"popup"` or `"both"`) and by tab context (`type` property: reading view shows types `0`+`1`, compose view shows types `0`+`2`) +- Ordering: always position-based using `position_display` (reading view) or `position_compose` (compose view). Alphabetical ordering has been removed - Special prompts retain their colored background (CSS class `special_prompt`) in the popup based on `is_special == "1"` ### Context Menu - Dynamically built from all prompts with `show_in` set to `"context"` or `"both"`, filtered to reading types only (`type` 0 or 1) - Appears as a "ThunderAI" submenu in the `message_list` context +- Ordering: position-based using `position_context` (fallback to alphabetical only when positions are equal) - Special prompts (add_tags, spamfilter, summarize, translate) route through `processEmails()` for batch processing; regular prompts execute via `menus.executeMenuAction()` - Icons: special prompts use dedicated icons (defined in `contextMenuIconsPath`); all other prompts use the addon icon (`images/icon-32.png`) - Add Tags in context menu assigns tags automatically (`addTagsAuto: true`), while in the popup it shows the interactive tag selection form +### Menu Order Page (`pages/menu_order/`) + +Dedicated page for reordering, enabling, and disabling menu items across both the popup and the context menu. Opened from the options page via the "Menu Order" button. + +**UI layout** — two side-by-side panels: +- **Popup Menu panel**: sub-tabs for "Reading" / "Composing" switch the list between `position_display` / `position_compose` ordering and between the allowed types (`0`+`1` vs `0`+`2`) +- **Context Menu panel**: single list ordered by `position_context`. Items with `type: "2"` (composing-only) are never shown here + +Each list has two sections: +- **Visible items**: active for the menu (`show_in` includes the menu), draggable to reorder +- **Hidden items**: inactive for the menu (`show_in` excludes the menu), sorted alphabetically, not draggable + +**Toggle coordination** — flipping the checkbox updates the prompt's `show_in` with four-state logic: +- Popup ON: `"none"` → `"popup"`, `"context"` → `"both"` +- Popup OFF: `"popup"` → `"none"`, `"both"` → `"context"` +- Context ON: `"none"` → `"context"`, `"popup"` → `"both"` +- Context OFF: `"context"` → `"none"`, `"both"` → `"popup"` + +**Drag and drop** — native HTML5 DnD assigns sequential position numbers (1, 2, 3, ...) to `position_display`, `position_compose`, or `position_context` depending on which list is being sorted. + +**Exclusions from the UI** (preserved on save so data is not lost): +- Prompts with `enabled === 0` (disabled) +- Special prompts whose base definition has `show_in: "none"` (internal prompts like `prompt_summarize_email_template` and `prompt_summarize_email_separator`) — retrieved via `getHiddenSpecialPromptIds()` +- Special prompts whose feature is not active — retrieved from background via `get_active_special_ids` message, which calls `getActiveSpecialPromptsIDs()` with current prefs and `_sparks_presence` + +**Cross-tab reload** — the page listens on `browser.storage.onChanged` for changes to `_default_prompts_properties`, `_custom_prompt`, or `_special_prompts`. When one of those keys changes (e.g. user saves from the Custom Prompts page in another tab), the page reloads its data with a 200ms debounce. Any unsaved local changes are discarded to avoid overwriting the other page's work. + +**Save flow**: +1. Re-concat preserved prompts (disabled + hidden-specials + inactive-feature specials) with the UI-visible prompts +2. Split by `is_default` / `is_special` and call `setDefaultPromptsProperties()`, `setCustomPrompts()`, `setSpecialPrompts()` +3. Send `reload_menus` to the background to rebuild both menus + +### Alphabetic-to-Position Migration + +The `dynamic_menu_order_alphabet` preference (previously a user-facing option) has been retired and removed from the UI, but the key still exists in storage as a one-shot migration flag. At every background startup, `migrateMenuOrderAlphabetic()` in `js/mzta-prompts.js` runs: + +1. Reads `dynamic_menu_order_alphabet` (defaults to `true` if unset) +2. If `true`: sorts all visible prompts with special prompts first (alphabetically), then the rest (alphabetically), and assigns sequential `position_display` = `position_compose` = `position_context` numbers. Hidden special prompts are preserved untouched. +3. Persists the new positions via `setDefaultPromptsProperties` / `setCustomPrompts` / `setSpecialPrompts` +4. Sets `dynamic_menu_order_alphabet = false` in sync storage so the migration does not run again + +This ensures existing users upgrading from the previous alphabetical-default behaviour get the same visible ordering on first run, while subsequent launches keep whatever custom ordering the user has set. + +### Special Prompt Visibility Dependencies + +`getActiveSpecialPromptsIDs()` in `js/mzta-utils.js` maps feature prefs to active special prompt IDs. Notable dependency: + +- `prompt_get_calendar_event_from_clipboard` is emitted only if **both** `get_calendar_event` and `get_calendar_event_from_clipboard` are active. If `get_calendar_event` is off, neither calendar prompt is shown regardless of the clipboard pref. + ### Summarize: Dual-Mode Prompt System The summarize feature uses two distinct prompt pathways: diff --git a/claude-spec/05-options.md b/claude-spec/05-options.md index db0fad58..2bf83ce4 100644 --- a/claude-spec/05-options.md +++ b/claude-spec/05-options.md @@ -64,6 +64,7 @@ These are generated programmatically at the bottom of `mzta-options-default.js` | `chatgpt_web_custom_gpt` | `''` | Custom GPT URL | | `chatgpt_web_load_wait_time` | `1000` | Wait time (ms) for ChatGPT page | | `dynamic_menu_force_enter` | `false` | Force Enter to submit in popup | +| `dynamic_menu_order_alphabet` | `true` | Internal migration flag only; no UI. Set to `false` by `migrateMenuOrderAlphabetic()` on first boot after upgrade to bootstrap position-based ordering. See `claude-spec/02-prompts.md` for details. | | `placeholders_use_default_value` | `false` | Use placeholder defaults when empty | | `max_prompt_length` | `30000` | Max prompt string length | @@ -123,6 +124,10 @@ The summarize settings page provides: - Each has Save/Reset buttons and placeholder autocomplete - Default text comes from i18n strings (`prompt_summarize_full_text`, etc.) +### Menu Order Page (`pages/menu_order/`) + +Entry point from the options page via the "Menu Order" button (next to "Manage your prompts"). Provides drag-and-drop reordering and toggle-based visibility control for both the popup and the context menu. See `claude-spec/02-prompts.md` ("Menu Order Page") for the full behaviour, data flow, and exclusion rules. + ### Translate Settings Page (`pages/translate/`) The translate settings page provides: From 1eb1ef6dc1fbb1dd9b9386f6b91f6c3ab9b6fef2 Mon Sep 17 00:00:00 2001 From: Mic Date: Thu, 16 Apr 2026 23:16:32 +0200 Subject: [PATCH 21/37] defaultContextMenuIcon set to empty. see #680 --- js/mzta-utils.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/mzta-utils.js b/js/mzta-utils.js index 47a68a72..4e80f6b3 100644 --- a/js/mzta-utils.js +++ b/js/mzta-utils.js @@ -43,7 +43,8 @@ export const specialPromptToContextMenuID = { 'prompt_translate_this': contextMenuID_Translate, }; -const defaultContextMenuIcon = 'moz-extension:images/icon-32.png'; +// const defaultContextMenuIcon = 'moz-extension:images/icon-32px.png'; +const defaultContextMenuIcon = ''; export function getContextMenuIcon(promptId) { const contextMenuId = specialPromptToContextMenuID[promptId]; From 43fcc972b5769722458464b24e9eb9f1f926e249 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 17 Apr 2026 01:12:00 +0200 Subject: [PATCH 22/37] code impagination --- pages/menu_order/mzta-menu-order.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pages/menu_order/mzta-menu-order.js b/pages/menu_order/mzta-menu-order.js index f49e2c94..93ab37c1 100644 --- a/pages/menu_order/mzta-menu-order.js +++ b/pages/menu_order/mzta-menu-order.js @@ -16,7 +16,13 @@ * along with this program. If not, see . */ -import { getPrompts, setDefaultPromptsProperties, setCustomPrompts, setSpecialPrompts, getHiddenSpecialPromptIds } from '../../js/mzta-prompts.js'; +import { + getPrompts, + setDefaultPromptsProperties, + setCustomPrompts, + setSpecialPrompts, + getHiddenSpecialPromptIds +} from '../../js/mzta-prompts.js'; import { i18nConditionalGet } from '../../js/mzta-utils.js'; let allPrompts = []; From 0ee53d6b37f6abf370555bcf49f5c5f2b2ba09e8 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 17 Apr 2026 01:12:00 +0200 Subject: [PATCH 23/37] context menu images added. see #680 #184 --- images/custom_menu/adventure-game.png | Bin 0 -> 1839 bytes images/custom_menu/calendar.png | Bin 0 -> 1402 bytes images/custom_menu/clapboard.png | Bin 0 -> 1390 bytes images/custom_menu/clock.png | Bin 0 -> 1691 bytes images/custom_menu/copywriting.png | Bin 0 -> 954 bytes images/custom_menu/customer-service.png | Bin 0 -> 1286 bytes images/custom_menu/deadline.png | Bin 0 -> 1925 bytes images/custom_menu/express-delivery.png | Bin 0 -> 1366 bytes images/custom_menu/home.png | Bin 0 -> 1051 bytes images/custom_menu/info.png | Bin 0 -> 1357 bytes images/custom_menu/invoice.png | Bin 0 -> 1283 bytes images/custom_menu/justice-scale.png | Bin 0 -> 1387 bytes images/custom_menu/like.png | Bin 0 -> 733 bytes images/custom_menu/love-letter.png | Bin 0 -> 1025 bytes images/custom_menu/policeman.png | Bin 0 -> 1266 bytes images/custom_menu/printer.png | Bin 0 -> 677 bytes images/custom_menu/puzzle-game.png | Bin 0 -> 1166 bytes images/custom_menu/scissors.png | Bin 0 -> 1560 bytes images/custom_menu/send.png | Bin 0 -> 859 bytes 19 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/custom_menu/adventure-game.png create mode 100644 images/custom_menu/calendar.png create mode 100644 images/custom_menu/clapboard.png create mode 100644 images/custom_menu/clock.png create mode 100644 images/custom_menu/copywriting.png create mode 100644 images/custom_menu/customer-service.png create mode 100644 images/custom_menu/deadline.png create mode 100644 images/custom_menu/express-delivery.png create mode 100644 images/custom_menu/home.png create mode 100644 images/custom_menu/info.png create mode 100644 images/custom_menu/invoice.png create mode 100644 images/custom_menu/justice-scale.png create mode 100644 images/custom_menu/like.png create mode 100644 images/custom_menu/love-letter.png create mode 100644 images/custom_menu/policeman.png create mode 100644 images/custom_menu/printer.png create mode 100644 images/custom_menu/puzzle-game.png create mode 100644 images/custom_menu/scissors.png create mode 100644 images/custom_menu/send.png diff --git a/images/custom_menu/adventure-game.png b/images/custom_menu/adventure-game.png new file mode 100644 index 0000000000000000000000000000000000000000..4887f773c7931e2660f5e0c8790bcf367cb641be GIT binary patch literal 1839 zcmV+~2hjM5P)KzQR0$#@C6tP_(^w6VR{?=a2ze3m&duw-&))rTxhMCUOMq!- z+VyF#wf=km|979Y*WMTY$0lwSU-alx_ef3NFQk}(rlu%_SCg_`3DKnqReiFuycSUZ zRRQ@c9-pQwW2=JI0@7~)ZwEe720LmGm$m*!0v4@)(jncvgy1Hb|O$@q^4Oh zsT7}|XkgF2Lv;1@hr&*jwz}re@BZzE0v0S?x5hNh1E7Sa8!Wl6m~GEJfjw^g7x<#C z4uE?L3dZH!9xt!IwU_GJGojFskmBA`$M${|j?=@P{1uN+Gkv})P&&Y~-`l{Co?1_g z&4{*9v|YUlKxSInxSY)}c%Zm|QJ0GgjV%DN2ujkXf2;gzQ>{0ufIHG>S1Bda0G@cP zl$FaKbdwAWq zr3}mm$jQ!R^{Nt-Qb;MUDUlDTSG*kUJ zLO^cr`WR^j<^s&hnND)@6l=22@1Kx3;PZ3(gTst$d761u`*95o(C_we=hl}aaLXQC zO!AcAiIz&N<=O$z0F0?>GbjyUUFjnc6a9XF)V?aEz3!4wQmPg zhr0nRa!u2i^Xf06?$)A(bH@ZpdA}t9gvDx)QuS`HCrl7*uQNHGvdD^s$N~E9F2<3T z5>-H2N^+PNJYPBiUJ5WTj`|3;2mKY#eWP)8q`$loKqfmG$Ko zxuzQw{QOmmqOG%&wvG+}vhGa7F=ec_5F}bVpipC_E=1J+?DBZ1!(s2+h*Giw51pey-#;>dZ)AWs z@;37`xtq*^W{O|=X~dk2)U=3uqU}o8SW@A%c0dTx5{kQBW8XX6oL-anPqYwl4O`?P z+qw8f)UWms6Jv`yH)NgNz1CO=8Z7||y)u-zaItCJM1bG?qZcVnhPxVZ4O{_0*A15E zWpd=11a-Q9f=li&QJ7w>34 ze)kXw3CZl<^Z?r*N<}Dt)WV8p$3Cm3w?Ft$QScGh-i_8>Aavu|(0J$X_C}n9{-Hr) z4UN|~+{cGMC?eYt>=ka0m!Z*7Ow*i@+~xN0-oc|G*Mq^%kYioPT}@|OQ)c9@L8+9! z0Vgpw8+Xs10YGVPG8+mrNQ;k&Sh(d%JI=vD;_P-zQ=;oS+BZI*REpPj{ei~I!M-8{ zJ8F)U9Sr5QaIhg_ON9^tfb#txbE@tP6IpX>E0j*Ar%KUZs8W9hY&dX;8Q*-8@S4yr2#OKv@+-|os>#pb(-qPL%!1T;aD`rZG z&mX|!^|HIHfZ~AwJ%U2_fk3ck;?>zoq(A14^1m(^R^qZL9suzRGXVUH@6WaLL;5 zm`d$eN?P4z=JX6UtXawISu?QP&tv_@CcWh+rtEDe%&y>+wkfB zL(zC9JitYdY|ixhhd-8PVBti#>zQWgb-Hd_RsYHR9oNI$)L*lOCF_<*lQ*R)=YiBF z1PdW`%{Zep^}^{-4^-V~?OXbDT3A?U3nb-iM9NhPH4~-cgPdX*#6?hln&l8>6%`c| dH-`Tw`!^4<#Vb4`IMe_D002ovPDHLkV1kHAktzTH literal 0 HcmV?d00001 diff --git a/images/custom_menu/calendar.png b/images/custom_menu/calendar.png new file mode 100644 index 0000000000000000000000000000000000000000..34842b221e1333d8d7d19623657ee4803422af69 GIT binary patch literal 1402 zcmV-=1%>*FP)p&}W5kPs=%z?2B3hfIWI zSYhG^O9J~4m|FB8=p_`X6&V92CP^bYzGieBX6~JH=RVHaeYoB`_s+~6XDoXBKkc*j zT6_J^+H3vKfqxk?(g6MCymbV_v+&ebx%aCxW&QR~vl%n`O?|*;`_8}DgEb?b@nQhq`Wx%0so{t- zJNZKU#*T7v8~nRQEen4WAslAw)u%eXPAo{;hV<5i)K9n6Wu9!BGtPkWnakJg$`TdQKtPA0!Kd3uNSaMQ7iwM}!z zIb{i~`RLLk@BVPL(y*-(`@FV2JlJ!LT^$b%#hruQ3xSu(b}E{khLXKsuVkWkwg%n? zR+S}iIgwpAnEhm`o|CEiA(NMq7#x=zcRqko&LNfYqQlB^lSq5oKO}*F3T3Zg#njNL z+K`%688(?QKJd3Eww3YwcS8|qoaAqtU+W`e8&pO{E*HZ<2mF_>pg&HvIb4pX0H}%h zm{Oh7hP10`ZjIw)T$Bh?YeS3;M_CsHm>#>Et&B4}~F>Szs}PKNCDDG2+@T!HKH zyDWzhniuy(M>z)3-P$WcYBe31p$tN__0Brv`**JSiPBglbE>mkmw8hf^ zC8JgmG-(*)$4PrALuiQk3w!ys>uG|vK}B$&@Rm@tx1C$6;p6=C+-i%r?h9dx5DS?^ z{D0%8L)cpp_N^W;U)HwP9iR)%H@Dj2)deAdg!L005V6C(!C-xFeHrAbz6mcDb5i>(Pn33Kj<^3syA z)Q=keC)-wZ$E@(Uni}aDob%4pKGU(^FD3zS*H|;9MAO{v`n(PYx4j2EjTpk?jaBhY z_g8ccl~~Xz!%#nF%)r`dRg%cr%3;4&R?GP}K(8o7r5y!4Q@STpHBJG*H!HtlMH36`|KhnjD7PHcjne+n;<+!&;E4{@WTknB7#j z5)nOE_+KZG$IjC}v&X;KKrhtj21L3AIh^jT+ecHWa{h1dC%=ur+y^|RhX4Qo07*qo IM6N<$g4oZi-2eap literal 0 HcmV?d00001 diff --git a/images/custom_menu/clapboard.png b/images/custom_menu/clapboard.png new file mode 100644 index 0000000000000000000000000000000000000000..c07e6aab4da62b398df63b57cf9c3ff981dd213c GIT binary patch literal 1390 zcmV-!1(EuRP)mev+pZl6;t9Yjhyb7$t>^SPivr!$?FXrc?gaPS2e*N9vUn)*~ zG<;0rgBa}j>gc3XM~_bv)AWe4<-Ypb)(v@iIWtmICZ;uAX-WKi@BRd_6rNl+#l=g| zfPg>1@e|*thQh%Q7c5$Hq4M16AL9ZP6udFcnc^$dM9UM6+>(+P*Ug?Y<1trSN^(U22H~V7#uuwlwiofropytn+SzMh*ErWvR zNiuGn6Je$jMONlCR<2w^LV}H8FhtqDLj*%XG!0(bzL`)cgc9NK;bU}ry@&`ai zH5J1!=<4j|)Tz^$W*9*bQJ%QJz;?Uu*`UbGL0Y!&*oM~>6g)rE-g^wUdl zyE8BhgN{xwCr|!>X__QYNM`-|)fmAbmSyop*?#AvvexuRUEM%@05hN~3&uMfY}&LA!!S@H?Av#M{(%7m#kv)%0`2Xc=B-<2rd8za^p!L=HUqG0_f~f9 zd>w#r*yNpEAJg2d15j8rj~y>>xtIIw(=Tan>xwWuQ&Apq_9P4^O3%nG)%7+U;~nI9 z+$g1(Gj|3R6=xY37(mzC81HnFlaqxK#pCm4-ph4#cuAh%B-@>VP1Be)E1&b{tML2% zSjxgu7T(V0_u>LfNzX1-%A%&aj=X#iE>|jviHYRpWmECWiEQ>%OKz&0Kg$oN15!~)fdVN0hYpwYGe(LLgqo`;;B0_d{ z7QMYbbp1}QvGEFv78PQ%X?Swn_@33_$J;jC>#RMD?lYzG3gfBMKm{0cs2ocl%UK z&6oigGMI=8?pHgaCaSIOX+{Sa%T)gZ&8PqjnZnqW%Mi_w3P;sE=o*NqAjZERu}T$J zGj@Oxr!~Goyd4xPVgdAzedUa4bPH11lE1o(f|Me07*qoM6N<$g43aoW&i*H literal 0 HcmV?d00001 diff --git a/images/custom_menu/clock.png b/images/custom_menu/clock.png new file mode 100644 index 0000000000000000000000000000000000000000..1450641a403668ac566a75accec0aecfbc5e0c00 GIT binary patch literal 1691 zcmV;M24wk(P)+q(w0U#l*s){#`aXvb zcHFqNleR;G|Cg(KpL71__Z(lJ=feM3>UxqMJQ9yMUhY?@TR=B~iYQTIpocLyUHg9c z$n#s3`(FpxbYkWKQSv=tBVa8O8Uyy5mi+9%CnFzxWPnY*iIpPyx4^vsK0|SL&|sxk z5ir0Dq=9J{PNZG>(lVc#Rq$&S3H|8c%_@6c0QdLC?^Do&5NWm*UyGVF8(=DhsUW9? zsni^GKHnu%L-CFSJ+V9*5IAmObsks~9K9xhO>a$a7E}*l-5oOdO1(+YR#=8niW!_p z@>1Uj-5Xbu&I+>`mz-PZfHGnCv}Q0X0O#}CTKme{;Ml?%232(5Thkqa`XgXRJ)cP!%Ll(BF&;pNyjY2VD z+ZRqxw^jw{>K6~F9|C-JnN1>{rT2WS5+?w!pB!TIcmGy#%~XmypW@6&3~^y=)CBP9 zy!t^^fJ7?sS)j4iXV4KWE-&(`=JGB%r~1sXOz_zSgF8HhtH73xuZmR>03}}nSW{~P z5Dj=)-CAFrUx*zP>Vma3{d6fvSIyYnD=<}EL8EnD$3#`tcnf}!QbpAQ^YeU%7}nDt?o%~Z=j^*P%w9}0 z@j#S!<8e;BKS{L4!^7*^DjNUm+$0{`tXg<1F-OnI!f~*1(t9wk+W|+-2b4ycQMdRPU@OOT?`zTL7wt+`GZ?6hq8H%>XS^&lq zd6M}efuSx!=^a=w9xVfNSq+jG_Vktyn1$*&{7-)J_+5Nsb4S&f%Oy%Un=PJ{!oQ)| z0fjLvl#Wo0`T(^Sbgv8HXiYqoqp{XkW_I{@4_#GJEy8#rLvzSaUc=E@4d7&LISj=P zxSAsilOj<|)@^QP&N8?#J;!VBja}({l}cvvymV%Syw;4kkkOzNufBFiBz_4Xn0W=5 zm`}>(^r(xXXWXH+K0w!*Jj==ye&IpI&S!o(DKW`4c!4s5$QtAt(i4^t`K z5|8II#}h7Z&Ro@}-J%lUFHy?hXm$AIasVJ__@7Zw`eNzp%f@qgVb`d`z??3b zJ%4J5iDag9e{m+wo@0Y$1$)oO=>6c*0k(JC;f=yZrVN8`EsVW13AZ;UfhXo~9mj2c z`&4>3=W3oB%5tRGi8U+g2?so-v}7jFR617%(AF5Fy`dIIVAlnQ7h`#Vyn?L{@~_UCpZiV(SPPJIq;7J;9d2l{>rSUMBRS#rs1F?B**D^}HCWUKJlLkeP0>Kf z$M3P|&%v`JIr>r>gjo#v@}V`6gIuO72mW8hw9XCnDAbnuN0!alZMOwN;gIPw5svL2 zB|e-&S&FqE53#eWnQ)!Oq@(#qO4FOve3&i$Y-deFcO6(2?Yp8@MF4=E170ib`SU^u zfSMX})a#{bddgKgmc|`Vffg_f+BP)M-cdF+#}w+R38QZJsa49USf?sLu@m;W;a`Gm zs~VTrxUn>PIzzC@@2ua_auShK80y7I6?%QK!b<}b<$ZeSiz4h2XsH^c1)X%h?*6=e z&r*KZ3;@86@u0^y_kDpUKrOp`Sy21(+i%>8Qc@}XqXW!q=YZEr`R*~04T7vdsf0ou l&1G)f%MPWNHuyi(e*lccWg>@|9k>7h002ovPDHLkV1g)!C1?Nu literal 0 HcmV?d00001 diff --git a/images/custom_menu/copywriting.png b/images/custom_menu/copywriting.png new file mode 100644 index 0000000000000000000000000000000000000000..6ba918fa6132da29e73d3e5bd8d6c1dd26039ab5 GIT binary patch literal 954 zcmV;r14aCaP)29a9vGyNJf`y6*)Efx~LZT-$8qfoB@T$Z#8Xz&z*hEQ) zK`%zYkU)$VFGh_V6yrr>f*LrPXhZ8C{&*;Vx-`)J)1|vJ^E|-P?(FRDOk2YDvb*oS zdGGuC{^sp_LPX^Aw;%IFh6T(pf7$bxyQRMj4t8FRniD{x1l=sm9RfyM#g{5R( zB#l=G2Rc7nHUf7~KG80=RPo6bTns}rZOPts+0IX+5HQ*Wcp}45h}PiyDzmPh{O3yP zm1QB&?9PKo81o!`U#0B&CoWAD2iiSnMM)k7NlVCP5>$PynGDm^?Z8*9o~x6#d&^PB zO;kTnJn^O)5F%G-q7SD@BhGeXa24Iyeg`vlH$F6 z_d*}p0owMZR4o~&>-?*UMrnqlnNX{yCC>KPl0XAg`+odW)P5S+FB z_lgH`Ay;7HTE1n`#|IpSdb=9zslE)(+J2`0xD2MZ0T_pcT_9rK`H0+cv2ic=-C7W{ z3x;~TLQl68#3B&Z1wpjxiX{^W>w+L+bVYmutza6$BM;kHM!a_@7J)w}Z#KOG(-8J- z9;DytxPJt;4)w;m-zaE1jP5s@Dlk{^_%B!DX)Dd;>BS{a&Cc=n%9k3mOcI2opQin0p}_sO2wS zr#v;Y$b!vLzJ81R#cK`rc?DEG5URy8zaRd3*$LD^)vJA-SHNH*Zl2@s?c$Sncg82+ znd1!vif7(D4}2ddkFQF|wYG-H>Dn#;ft9Rh+lO5htiuW~0@;|U+u@4BHyya|EL_Rr z5C8T1b|ZWl>Qi3guOls&&>$Z8KVi56PaU+JihV>4c^M^qKZ{;rMwQQtiNXPXKIjJB zp$a%v=QR}>S#ogzB_r^+4KL<)d*{Za6n&n|0kobclsL?zyMr?Z@GsrA#!ZjxGb~@kllh%k7&6!} zV*uC5?AKZgVc1jj2LbD644`DXG5e&a3;??9mKB@kLWx_0dVj6p$a6vJ=g*=dTv4<( z;V6#v#MykPpPO;%Nxy6d4AUA1IPz?O29cy9RC=x|dT-EG%P)t2did{u{P>JS2yDr=mJke#hn z!HPKuy>+tM$pKu^>e;naIO@Ua1B_>yaHcc;IdcthcmDJNOg?1Dfotihn_o-APzEp# zP9I?3(hmsuS5j$q=WY!xWm#{jglugTUv{ld!NK-Dlht~@jFO-2NomLN`x@`V3|9g0 z<`@66|74tmBUmuo$0wT>P#3arq_}w|7B~ScduH9^GuVRs!eH<_0C#YR+^Kl@)BvNg zIB3nqfiy?YDSjOea%L=02Q1TwjpcMbcR>HJH&Y=xHNk)z`r@Gau~E79LBT}mA=QO8oR zxAd0NE_oFDcAS3*JMf~0r=+B{rpBbD*^e`Bp%Mv%X`-bhbM-o+GKf8^jv*>(Zt*W0 z8Flv$4@>N>z6%158d~lx1yghgjqQKEC0+5mhAluVKvT0HA)wEWU`E22)e)?^8tnOV zvFm4pVK9gfA)u+rpOb3Uutf^<53{RxohJbMjSC$rEw>BH^yTLx;cT;h3N$h@uNgZ! z8#59{m?i-I+Rr~a1VA`4p|K$Yb4$)i7d@=P8B4uzcXHwkD3%CeOt^MWpH2{|HW5N_7K^V%8;i|h0M5!9(pmsX zwAPG{7C$Wo`U&9e(g2;>(21U~VNc|8GMXDV+=5(hEc?Y~l{=xnM6{!|K#1EnT!E8i z{Qa1#wGif=otax@u&tvC+kne4#g3n2Ud@U{IMCJCl7fr5M8OQGj9v>s9>5GPAdjzFs=z~{N8ao5X60F`nIAns*oZM5XC3ju`D51^&O z9gCwgo`=h;Jt#StpDEY^)0VHf8Sx`p3++nWvH0YABUwP@cmx$f+qQ8iKUL5Hw{y{P wGvMDKEbAU_`YmtE8Z`sf^O>ECe#y`O55S0~tI}o?!vFvP07*qoM6N<$f(jK{E&u=k literal 0 HcmV?d00001 diff --git a/images/custom_menu/deadline.png b/images/custom_menu/deadline.png new file mode 100644 index 0000000000000000000000000000000000000000..37041192dfbb6a3d49ec9ed2c387a42467bb022e GIT binary patch literal 1925 zcmV;02YUF4P)6sRCkp`=n2+>kV(B+g0_Y-e}9Bwpg#=WUk{V~;(Ko#h)6 z|1b02Ip?19zvtY0&KvlDfjaQe57Y;rUNBXz-?gU|F9(Y8YL%cuNEuOrVJV#6*#76b zL8zG-@FgTZbzprzN`6(W*}EJ-+)2#9ikIga!uvdkN0eaQ48(^)w1fIh+=#zNdz%AL zwsdF7$kFe|)Q7F}(6kY1S64|#+fANEw;S1df*)en~#CCyt z0@wsx{KBfrcbaNK8=hVC>Us=qGp=}BtlN8b#uAv#+a6Edh`(tD!iHw}01%o)ubzc5 zyBrbnBmE{`GL0KcV_zP?9g2-FZxpECPjXlH-jVCiS+?^Eizq_zFw(Pt9n)sOY~H%v zvQjT-nnAFn5)tqbTs9wHQys!*%(!iLI6?ZKXUU$u0;=#hD9DLJBf+0(BA>pupFvF1L<%kT75PSC! z-e@ZCH#Itd>tqS&vue_(e|U9T0!H)pC61LiqLe44W-x0*D}j|wAYfJ0$G7W?X(-NB z&2>FSM&d-HB>;E|-tSNIT6copjEk4f694l-oW5ZI94m>HN%Xp>elmA*OgZ_*^Rl~? z@+3gXvLysoHK9p(rlFYEmXuMNiQ~8~06o3e`OBU|6FW?$SrdkLRz_)^O_*%Xe6)Sct*C_>P*t7{d z65z4g5MQq=${WxBL=C~99{|(TnKL_**UheqFuN-EE(ENt@Uf>aL(=cV8%g30jsiq{ z%74VV@_uJw1{$A@y0%pd5N@o;FHJ%|ou}s&aq)6LS9-2Z%^XM1^wIj%TQ|u8I*xat zGFiS=pCcbwK7T@~)RU8KC4d?9e`EaGM4J}_@Y&gZN)0(c^5|h`(o!pL8*3Rn{-beufgcz70kXv9o9@6wtdru{rR?x2i z@S8esJin1e_eFW?`M(zELLq;CU0*uaxV&U0ua`>pjz8-FLW)k{A%ILePLV%MPt3t@ zNF2>U3PC93XDa+Yoi|?CNav;D0{P(3D5a%E=$giXp)3GS-_W?)iowSP8Ia2S*?6}a z?&=2M-Lo+)M{)AZB|5vWG86o!&i##rx8%Twrx+fIb1r6cKH;FUHcroV02H)y=sW2G z0@)L7ol-MK0fvqo!~ssU zd$Mi5xSBM72jDpl{hen3*s*T_rQllJA!93!o#-MtR!i6Q7#y6q9apYiE9jVSUQTg% z$U~WD+UUZdpl4W3o8@H&cqHS|Mk88$po~C$97MjyFTf;_6C$<nWPuAw zhxWlNf9uPTwj5;EV`$%DMnCGna~(h^(;s@=I=Zs>aXR}MKg*5gt$(ns)MMjQQtCR92U;kr zZy*pXMhb!Sz?0y3F3I6R22Y$M)_q~Zxsa}|`=7Q?z4)t{;@t9ofw5%UGp?20>A7~` zR(8|5)eQ5ZX6TPv9X~#KYs=fBO*dMspSzCz70L3p~6CN10{QI)&x@00000 LNkvXXu0mjf^pKUB literal 0 HcmV?d00001 diff --git a/images/custom_menu/express-delivery.png b/images/custom_menu/express-delivery.png new file mode 100644 index 0000000000000000000000000000000000000000..d115986a5894c5445c06db01264df779a451819a GIT binary patch literal 1366 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 P00000NkvXXu0mjf4>yH| literal 0 HcmV?d00001 diff --git a/images/custom_menu/invoice.png b/images/custom_menu/invoice.png new file mode 100644 index 0000000000000000000000000000000000000000..01ecd7c9512db7ae17b2430cdc5d27d83cab2241 GIT binary patch literal 1283 zcmV+e1^oJnP)ji4Y^gcDGDKycvRLxZ^XT*QeB+(3vU z;)2vxy#V3_LgGNh0YOUYFPAa}gZ08S?@Wp-8)Nc(A3ajHW?S{YS&F>6dyq=7z>Ru*a*Mnl+ zNk)~kxdZWUI*C`BJ9ihYcTL-WQxRfclLNTEjFf8L*Y}p}vwH?`J%4P!6F1A3h>GQ0 znOt_x?G~#wccf3xW)exka`B}M9c*X{DMQ=V=SxJ{v@cFBI~>VGE_VdbtcvY1K5y;_ z5Z6^kbEy`aKp>GV5O*JxA09oF{BchJDMRCxqdj5nQ1vHOOvi^!ZlP%6Q{d#D0QReR zL?Iqg+RejZ@jC)kY!9pMp$S26Tt{qqXggN-o&hSh$Bmh_jg`!$4f1`leG}{%z^Z#l z;PkOx#urR1#|PlrbP22O@0xfxZE&Om;7|Zf1p7EXZ&I>d(lH%?XP!tmj&@dF zp;5hYja=Hma(vP;om`&*1W!jQY|uUw3Lpgk$)0x%;CPZhX3JQ04^;?G3?#@T^#2c_ zRCTfH9wP$@W{Wn9Wrs{c=iYLaS_egVAQkBdc*`5(|5oSi!F&=9I_1LAvm>By;$0bChW`p7_nZbQTKgHz$og2ni} z$q!P#WnbCW3|pp+GhZu~-b`7xGJ5KKDWa;<}0euKrWxQQ6rz zVn=f+P9N*#<&&lT?J1~fZrB-=rFuE3WfpW6wdN*}I?gkj+A(@hiEs6lBiy2mMNA-F9A_oBS+v1@ zG5;}w3v_isG6_*5C}gJwYh^GifcQv^O}^JY_OSo}$jkY+kbDR{*EYkS_~L!>_T;zQ tjeBeW8z7g4^GKdFTK)h4 literal 0 HcmV?d00001 diff --git a/images/custom_menu/justice-scale.png b/images/custom_menu/justice-scale.png new file mode 100644 index 0000000000000000000000000000000000000000..6f52915e75cb35c350123d7db9269ad9b4ef3f7e GIT binary patch literal 1387 zcmV-x1(f=UP)eNI)d$gBn69B~U1>QEG)!3lzH>+Ah0yyJh#zy)*Oofr8s|%WgNm z>3Q0jIdlHMnc2N_7x=G>Wjb-s+WEfQwrUB-B!(hZ)v-&xgPG)LnF1&mTgC{bL@0G- zYs)gt$ryk^xpLeLQ?eM8?eb)vGYRclUp|MVYq`d#%r_&uzR2sDWh89;5r{_;Qhevl z$vOv3v}`-l^=C!^em^w3qkIWdnn#e*HO6uQ4-00hA3G=tVbM1zVbq%%#Brc8Hb*Ezo*>RyhX3=nE5d!I4#19G&79H|LF z(BODW&B->8M&=TUY#K zS*_dc?)l;q|0cJ_dkK9KDFXzl#ozYLDsF##dbbG+?ZCxA`>UjsnhqQ~=XmHosk z4gLc{*rhkRyN)*haP-hjn(_UZg^kDSA}y>8z6NP-Rz z#UzY7YQwi3-ABfILWZ#dL?;2908k$BdQ0Xq#*;k{{0yguLK~%&Gf9X|O3G#_M8kN` zLsyU_Da2MOWfP>_ETzny9uB`e-ZKV(h%_n0J3@$qd3kw3p;V&~;>BPvSotsrC-n~u zED%Dhlu|b4=H>>35Z_44+CoHQYL02#3E z;?iuN?*ssFDR&Dg=k)dWubmKpTlU-BvW^uO6kNXF$L+n`ve)(Y_RdYW_|?9?5^jmw zgl+F)j1d5o6c%=KZXdVou}69z;J2>s=Q-!kB}{W~vfy zc{6z9#?j<{OIW)&=g0S?d`Ku6+hqE zna?@j$hoCBl0Ms#>(G-@8zb@8 zQUTlv8*duXXv~Vozj7KkBGE=88eMg=rKJJ@bZT`kl5E9U#uNkav%KJPn6~oXhi$S= zhijqO?i4QD^p8Q+TSuF5=d5Y~0Fl;47u`#A9@Zk7OH2D&W<)VSIF6~XK0@hVu$s_R zMEAVJuO<$8cLI11DX(XC!Q;~R1E%As&Zq4+<6;efg(->?fDdx&j2;~Ta_Wp7=I40_ z71%s5a@se5u|Q66u#NdASUv!@CKMw-mL~Fji=a_~b7>|uP^ahBp&lUVG8*mZNkN`c zcL4{_rt8o6mF(Y=p4xI(r?yrXYf4pU#9RO{5aWx3Q43Su3Kd0Ji{DtW8~~sgNnPq4 zOFwPuOD8w2uk7*Hm)1b39|2^i$;%nTs@3~jPp2}bwF5)L=Eg}B$EbG`fRq3e?!c)n z)w4jd0bqs0roC85Dw+fUbQkNN;c>STqkab0?wUQ#w;X*F1q_WOj)0Ky9}X#~XlR75 tA8l3wSUx#`VQW^V#U14QO!!{_{sz|mau<55$bJ9-002ovPDHLkV1n_KgD(I8 literal 0 HcmV?d00001 diff --git a/images/custom_menu/like.png b/images/custom_menu/like.png new file mode 100644 index 0000000000000000000000000000000000000000..3625b936b5af1b4e5efbc0605e4c5334d2d4e1a4 GIT binary patch literal 733 zcmV<30wVp1P)C_gxg>Xa?{i2@OzyC{4pmju80QB3}0Zao(064*w;=H@~^g{0dz&D$R(3E4( z2CbrpvR;2Z2B=d_G1mo%hJ)4$*8w4Kc%U2?AhNp-z#m>50O(3ntRlfu0z!Q2IsjBG zjq1j00F=wsV!GJuJ^*8?H6#nQ&Xf4qRRDg!a2|w{t#M^n0m#tSy9U6SObkqO-ULJ0 zWq<%Z#v4Ha8o%L#-637L^8}0o=&MBw3|@+B<}@gCAHb-`-*KTO`#_o~Avu)d%qy>p zMh>L&}h3y1=+!g?bR@JzUv3jJ25Ur3jhp84e&K$0D2cK0An>;06~!g z>|tcBej@fBfUrt5qy7v-J;_3g0aYx1`a4tyP>u^>#C+!|$Az<#g=SfxxCt^0kk)?l zSV}0ngv$3HpoFpF3m|{m;J69W1wgesiWyvyP$dU4mxs*d+cx!+u&k_kaq6Wn0BNGA zN@dG$q7~!ny~&-Jn|r26o^Fjd01W*$7v0AIQ_~kjT-M^ZaA;sbb9oX@mh1 P00000NkvXXu0mjfXW>6d literal 0 HcmV?d00001 diff --git a/images/custom_menu/love-letter.png b/images/custom_menu/love-letter.png new file mode 100644 index 0000000000000000000000000000000000000000..37529e46c66e97674fdd3c987cfbf823dc0a1227 GIT binary patch literal 1025 zcmV+c1pfPpP)NUL;TtKvsCcn=n+jgk%SjectPKziuqx_MJOdB|e<2OH+W~Uo`gmlI&`) z+x>We!X>ox3-;qpNK;5t+>IN! zpLW26q#1bLP^?yKtH=wB6H;qgm^b!>q$%$DC)mxOF*B4TIOiDc?lBrUg2&f6`~G_b zaa<|HS+HE>l^g(94F~MYuX4CKWAyu84tHtF@%KM4`1Fg}lgoF-QG6-{l;Qa%`v*DP zjYhcCLfmh)x1h@)#-d{gI7d{%ML{C0Zn+H`2<~mRm(t74HT-d0E z$CtJ_^TPB0KLoWmAP9)AyiTz7R0$ql+@$-#yC?`M?U&Rn1pZr9E(Br7sW-0B+S;7j z{`5tXkFF6!QT;Wn2|?)^R=H5>MiSkTV6g*fnsRjePh^HoO05aWuzX{IgynM? zskgF9G*cBDgkXAp?-XJdP7yvGXX3_x&uk)A7%<-fNwnK-R=VB$vOg1O-a(oqp#w49 z$jtcX?p@3b1d>g@-Lja<$V{?vjD^e~2spF6Jl#hs{A{UpAPd4o^>BEPgZ+J+jcxWn ztLDa{0nYdPJk;r&%mD`ko`rF|M8Dr}oauF~xdRRqEGj7T;pUG|oCAf;kB^Vp`(qF1 z7KdPxaFYK!b$>EKR>7jE0ipMt8faRf`!~ z6n?MOoHLm%F vDE3X3C2gtivURkNxz8?bZ+}zAdZ5pL4bG63G!DAy00000NkvXXu0mjf%ZJ={ literal 0 HcmV?d00001 diff --git a/images/custom_menu/policeman.png b/images/custom_menu/policeman.png new file mode 100644 index 0000000000000000000000000000000000000000..847c2fc6b64c9169f0f45021cf8737a349598819 GIT binary patch literal 1266 zcmV?x6kdv3Q}kl4G@SSyo{Qf#wfmu3B*t$i3x%q2*Cs-hD0&y z7x`qO@r@=z@Qa9OL`#Segv2ODpnwGo)l#etkCg6q%Wn7HJ2S@*Z3%SS?yYc>N$#Eb zpEJKXGk4BWgnXKs#JN!0_7qk%qL>d%1u6!IlfXIPh!WXzN7uByJ9jD*#;*z^x_|Yl z#iG=^z+Ite73ajNb-!ulhJ9>(cV?JU;vL1&lb? zn+nK^mThys7|Xw#4?y?pw}yjiB`mr7JIbXGaU7;hyvX}4s{lQ0ak3e>IRL*ukep5h z2hKFGq;4-Z<8q+A9=|^r$voF~ya2|xn^3Y1P)>nEtR#vXb@;G#10{jQdJZ3Tt|3+$ zMSBMAX{@mTRw-W1=RedToblw3P0@Jij#xadL9-)D1fW!{?155m|T4)Q3+U36R@Br zohuZWTwTW5^IZtGD~xX^zXW=^AH2Nox#?>+JiD;ijVJ~-eB@R?`yK)vT-lXMZlt5b z=JOru5CU``{3_!*k)kkKZ6s6ua8pSbZ6qv!iyXpe!Za6iFs$Fu0OgsoC1|q?t;y5A zF3-fDx#a+=vC$J7Y|poPYl7{nvC+Hb005PbKfhbFc~7)vq=pVYsd{2%-VFG^0RUeA zVA958?W#qz4<-N;Alm21-_FTpU)H@mUVq~PXjtDdbH7{l$ByGwbG<|Yzc+>7o5D*Z z*mGsoj|)Qd=j2(2_^z2cQW|Fw`e55HY=wI}gtwl}5Hl?o?baZT$0 z=|GW9rlwy_#7@`0boS^g_iZ}&$n-ryQ7pZcGaOLM7_mYc5kT6AXMQa?9nI>=k+fn; ze<#)mnVuxt%W&*GBvcjE@qr}Jol+d_5VV&ebETJ9ymdW92$YTqpoi{(ybDV5Gg^lO5f@=HnJk(dNC2J$AVvOoO>IGA$EuO$Hi#QwGjmrcYJS`*_W}To9jgYV zKLaRcnmDM@1&I2G$EBYDAUw0=HZzLMz>-H%g11l8dS^g0HhF?D?rpKj?%44c-(maHW2(7nYrAo z>Aw^L@FxHuC3gY39{?~HTfHB5o|U>m08LFO+{MgoXc&IWFdPn{wZg{6zdqyO@BsGM zhExhtN=PYjacvFt%}pP`4P+k)p}jWEyA6A<$~`p^bt&ncbZIPIf^-RnVR(ax0Qfcm z0y9qy2r>1|X&mX&SUL3Gb@}$!*Xw&;E#gY}%NnJ+B`%kWkvU>pG9u~&h-@q)bQI?x z;v68TLqu3)Ku{+xe4>YmW!o=^@d!yP!8js;P9dLv9``Ug*UI%5r8){4Da>=w00000 LNkvXXu0mjf5l$pC literal 0 HcmV?d00001 diff --git a/images/custom_menu/puzzle-game.png b/images/custom_menu/puzzle-game.png new file mode 100644 index 0000000000000000000000000000000000000000..a2cee86ee2e9bd6d462c1bb5cad39e30f2ca75e0 GIT binary patch literal 1166 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 KMNUMnLSTa8=*=kr literal 0 HcmV?d00001 diff --git a/images/custom_menu/send.png b/images/custom_menu/send.png new file mode 100644 index 0000000000000000000000000000000000000000..d6aefcd8be4663ba7daf873c427833de5d3fd677 GIT binary patch literal 859 zcmV-h1El2IW!)gdI3cs3G4tCMf60} z-x!`Rz`Jo+O;zCb`k@v8$&gozu+Y_!IuWu}p0L}Yi7(|O8jv$!ehQQx2!(4iX0tFk z0s2nG!9DtS?;fa@tbo%6n<;VY$sXuFQc06qhwm{_gv}0;0@Wo zhBXc5XJIK0ZnuD@QX;maL%1VJkW50lao^OP14mVjJH4+eQ8pWdA_81iCIgxVdSO>K z3EUp=`!Jcird5jDw*%#;k*f}j(+R2yxhxb4Aju-OIu5|+2gL$fvetp!7d1Vgs?Zr~ z-uob(hC~7}orD@Y;PyZu&>V1CE*B^=tgMJ3MfD&u$N=E=HXmM=LxpG*6a^q{EdJils*l**^wV(mHRh7002ovPDHLkV1ka$b07c! literal 0 HcmV?d00001 From e6bc740adfa1d711d8b90492aa46b1684a91f28a Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 17 Apr 2026 01:12:00 +0200 Subject: [PATCH 24/37] README updated --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f52bf24a..dea7963d 100644 --- a/README.md +++ b/README.md @@ -120,6 +120,7 @@ _The language status represents the percentage of translated strings in the late - [Roundicons](https://www.flaticon.com/authors/roundicons) for the summarize context menu icon - [HideMau](https://www.flaticon.com/authors/hidemaru) for the ai summarize icon - [Hilmy Abiyyu A.](https://www.flaticon.com/authors/hilmy-abiyyu-a) for the ai translate and context menu icons +- [bearicons](https://www.flaticon.com/authors/bearicons) for the empty context menu icon
    From cfc0aedb982acc0330758512772f16fa95f3ce07 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 17 Apr 2026 01:21:00 +0200 Subject: [PATCH 25/37] context menu icons added. see #680 #184 --- _locales/en/messages.json | 8 ++ claude-spec/02-prompts.md | 1 + images/custom_menu/empty_icon.png | Bin 0 -> 705 bytes js/mzta-menus.js | 18 ++- js/mzta-prompts.js | 5 + js/mzta-utils.js | 19 ++- pages/menu_order/mzta-custom-menu-icons.js | 41 ++++++ pages/menu_order/mzta-menu-order.css | 103 ++++++++++++++ pages/menu_order/mzta-menu-order.js | 151 ++++++++++++++++++++- 9 files changed, 334 insertions(+), 12 deletions(-) create mode 100644 images/custom_menu/empty_icon.png create mode 100644 pages/menu_order/mzta-custom-menu-icons.js diff --git a/_locales/en/messages.json b/_locales/en/messages.json index f0a1af2d..7d45ba67 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -2314,5 +2314,13 @@ "menu_order_hidden_items": { "message": "Hidden items", "description": "Section header for hidden menu items" + }, + "menu_order_icon_label": { + "message": "Choose an icon", + "description": "Label/tooltip for the context menu icon picker on the menu order page" + }, + "menu_order_icon_none": { + "message": "(none)", + "description": "Dropdown option meaning no custom icon is selected for the context menu item" } } \ No newline at end of file diff --git a/claude-spec/02-prompts.md b/claude-spec/02-prompts.md index ae33e52e..d2268831 100644 --- a/claude-spec/02-prompts.md +++ b/claude-spec/02-prompts.md @@ -33,6 +33,7 @@ Prompts are the core user-facing feature of ThunderAI. Each prompt defines an AI | `position_compose` | number | Sort order for the popup menu in compose view | | `position_context` | number | Sort order for the context menu | | `show_in` | string | `"popup"` = popup only, `"context"` = context menu only, `"both"` = both, `"none"` = hidden from all menus. Default: `"popup"` for default/custom prompts, `"both"` for special prompts | +| `custom_icon` | string | Filename (with extension) of an icon in `images/custom_menu/` used as the context-menu icon. Empty string = no icon. Only used for non-special prompts (special prompts use their hard-coded icons in `specialPromptToContextMenuID`). Selectable from a dropdown on the Menu Order page, context-menu tab. | ### Per-Prompt API Override Properties diff --git a/images/custom_menu/empty_icon.png b/images/custom_menu/empty_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e7ebda097da62ea41addd20634a78c9cf22dfb3b GIT binary patch literal 705 zcmV;y0zUnTP)d0G#v0N;FSyf z1ewmN;3DlK%i)CYI|46&U0?~A1;&6;V9I>!0NoPsrVEZ^*d=0qC zfqx=fS0}(TJ9u6>oKvpgQgMwuEw~zRsq7!^1Ut&O8xWpKNBOjEu3yb9}HJBC;3JCe;g0%U6{n zCZ`|VP)-E(3X&s_nHfbe%Y@B~=&1vMQ%&_k~0 znmhDP>d57m*OIk8afO{sf?TQJkc(y7z+M2a&9^4;((vZWdI|VrwC#hBbN^h@ zYu-S5xT@eI3f3Itq3A2PVWfxi?GUXovelf`ZcBtb6caNOt`|#$v{7xOnfS&1Ar+e( nUyu{-X&JCiK8NS6_OIj?CsENi+{35x00000NkvXXu0mjfi_|nh literal 0 HcmV?d00001 diff --git a/js/mzta-menus.js b/js/mzta-menus.js index b7230250..6fd63209 100644 --- a/js/mzta-menus.js +++ b/js/mzta-menus.js @@ -528,14 +528,18 @@ export class mzta_Menus { // Create child menu items for (const prompt of contextPrompts) { const title = i18nConditionalGet(prompt.name); + const iconPath = getContextMenuIcon(prompt); + const menuOpts = { + id: 'mzta-ctx-' + prompt.id, + title: title, + contexts: ["message_list"], + parentId: 'mzta-context-parent', + }; + if (iconPath) { + menuOpts.icons = iconPath; + } await new Promise(resolve => - browser.menus.create({ - id: 'mzta-ctx-' + prompt.id, - title: title, - contexts: ["message_list"], - parentId: 'mzta-context-parent', - icons: getContextMenuIcon(prompt.id), - }, resolve) + browser.menus.create(menuOpts, resolve) ); } this.logger.log("Context menus loaded: " + contextPrompts.length + " items"); diff --git a/js/mzta-prompts.js b/js/mzta-prompts.js index 13c76b83..f81f9803 100644 --- a/js/mzta-prompts.js +++ b/js/mzta-prompts.js @@ -561,6 +561,7 @@ async function getDefaultPrompts_withProps() { prompt.chatgpt_web_custom_gpt = (prefs._default_prompts_properties[prompt.id]?.chatgpt_web_custom_gpt || '').trim(); prompt.api_type = (prefs._default_prompts_properties[prompt.id]?.api_type || '').trim(); prompt.show_in = prefs._default_prompts_properties[prompt.id]?.show_in || prompt.show_in; + prompt.custom_icon = prefs._default_prompts_properties[prompt.id]?.custom_icon || ""; }else{ prompt.position_display = pos; prompt.position_compose = pos; @@ -600,6 +601,9 @@ async function getCustomPrompts() { if(prompt.show_in === undefined){ prompt.show_in = "popup"; } + if(prompt.custom_icon === undefined){ + prompt.custom_icon = ""; + } }); return prefs._custom_prompt; } @@ -619,6 +623,7 @@ export async function setDefaultPromptsProperties(prompts) { chatgpt_web_custom_gpt: (prompt.chatgpt_web_custom_gpt === undefined || prompt.chatgpt_web_custom_gpt === "undefined") ? "" : prompt.chatgpt_web_custom_gpt, api_type: (prompt.api_type === undefined || prompt.api_type === "undefined") ? "" : prompt.api_type, show_in: (prompt.show_in === undefined || prompt.show_in === "undefined") ? "popup" : prompt.show_in, + custom_icon: (prompt.custom_icon === undefined || prompt.custom_icon === "undefined") ? "" : prompt.custom_icon, }; }); //console.log('>>>>>>>>>>>>>> default_prompts_properties: ' + JSON.stringify(default_prompts_properties)); diff --git a/js/mzta-utils.js b/js/mzta-utils.js index 4e80f6b3..ffa24ca3 100644 --- a/js/mzta-utils.js +++ b/js/mzta-utils.js @@ -46,11 +46,22 @@ export const specialPromptToContextMenuID = { // const defaultContextMenuIcon = 'moz-extension:images/icon-32px.png'; const defaultContextMenuIcon = ''; -export function getContextMenuIcon(promptId) { - const contextMenuId = specialPromptToContextMenuID[promptId]; - if (contextMenuId && contextMenuIconsPath[contextMenuId]) { - return contextMenuIconsPath[contextMenuId]; +export function getContextMenuIcon(prompt) { + // Back-compat: accept a plain id string too + const promptId = (typeof prompt === 'string') ? prompt : prompt?.id; + const isSpecial = (typeof prompt === 'object' && prompt !== null) ? String(prompt.is_special) === '1' : true; + + if (isSpecial) { + const contextMenuId = specialPromptToContextMenuID[promptId]; + if (contextMenuId && contextMenuIconsPath[contextMenuId]) { + return contextMenuIconsPath[contextMenuId]; + } } + + if (typeof prompt === 'object' && prompt !== null && prompt.custom_icon) { + return 'moz-extension:images/custom_menu/' + prompt.custom_icon; + } + return defaultContextMenuIcon; } diff --git a/pages/menu_order/mzta-custom-menu-icons.js b/pages/menu_order/mzta-custom-menu-icons.js new file mode 100644 index 00000000..df4ffba3 --- /dev/null +++ b/pages/menu_order/mzta-custom-menu-icons.js @@ -0,0 +1,41 @@ +/* + * ThunderAI [https://micz.it/thunderbird-addon-thunderai/] + * Copyright (C) 2024 - 2026 Mic (m@micz.it) + + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +export const customMenuIconsPath = 'images/custom_menu/'; + +export const customMenuIcons = [ + 'adventure-game.png', + 'calendar.png', + 'clapboard.png', + 'clock.png', + 'copywriting.png', + 'customer-service.png', + 'deadline.png', + 'express-delivery.png', + 'home.png', + 'info.png', + 'invoice.png', + 'justice-scale.png', + 'like.png', + 'love-letter.png', + 'policeman.png', + 'printer.png', + 'puzzle-game.png', + 'scissors.png', + 'send.png', +]; diff --git a/pages/menu_order/mzta-menu-order.css b/pages/menu_order/mzta-menu-order.css index b6afb6f1..81b5055f 100644 --- a/pages/menu_order/mzta-menu-order.css +++ b/pages/menu_order/mzta-menu-order.css @@ -171,6 +171,86 @@ color: #e65100; } +.item_icon_preview { + width: 18px; + height: 18px; + object-fit: contain; + flex-shrink: 0; + cursor: pointer; + border: 1px solid transparent; + border-radius: 3px; + padding: 1px; + box-sizing: border-box; +} + +.item_icon_preview:hover { + border-color: #409df3; + background: #eef5ff; +} + +.item_icon_preview_empty { + display: inline-block; + background: transparent; +} + +.item_icon_preview_special { + cursor: default; +} + +.item_icon_preview_special:hover { + border-color: transparent; + background: transparent; +} + +.icon_picker_popover { + position: absolute; + z-index: 1000; + background: #fff; + border: 1px solid #bbb; + border-radius: 6px; + padding: 6px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18); + display: grid; + grid-template-columns: repeat(5, 28px); + gap: 4px; + max-width: 180px; +} + +.icon_picker_cell { + width: 28px; + height: 28px; + padding: 2px; + border: 1px solid transparent; + background: transparent; + border-radius: 4px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + box-sizing: border-box; +} + +.icon_picker_cell img { + width: 20px; + height: 20px; + object-fit: contain; +} + +.icon_picker_cell:hover { + background: #eef5ff; + border-color: #409df3; +} + +.icon_picker_cell.selected { + background: #d4eaff; + border-color: #409df3; +} + +.icon_picker_cell_none { + font-size: 1.2em; + color: #888; +} + /* Hidden list items */ .hidden_list .sortable_item { opacity: 0.6; @@ -268,6 +348,29 @@ color: #ffb74d; } + .item_icon_preview:hover { + background: #3a3a44; + border-color: #409df3; + } + + .icon_picker_popover { + background: #2a2a30; + border-color: #555; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.6); + } + + .icon_picker_cell:hover { + background: #3a3a44; + } + + .icon_picker_cell.selected { + background: #1a3a5c; + } + + .icon_picker_cell_none { + color: #aaa; + } + .hidden_list .sortable_item { background: #2a2a2e; } diff --git a/pages/menu_order/mzta-menu-order.js b/pages/menu_order/mzta-menu-order.js index 93ab37c1..f2551e54 100644 --- a/pages/menu_order/mzta-menu-order.js +++ b/pages/menu_order/mzta-menu-order.js @@ -23,7 +23,17 @@ import { setSpecialPrompts, getHiddenSpecialPromptIds } from '../../js/mzta-prompts.js'; -import { i18nConditionalGet } from '../../js/mzta-utils.js'; +import { i18nConditionalGet, specialPromptToContextMenuID, contextMenuIconsPath } from '../../js/mzta-utils.js'; +import { customMenuIcons, customMenuIconsPath } from './mzta-custom-menu-icons.js'; + +// Convert "moz-extension:images/foo.png" to a relative path usable from this page +function resolveSpecialIconPath(promptId) { + const ctxId = specialPromptToContextMenuID[promptId]; + if (!ctxId) return ''; + const raw = contextMenuIconsPath[ctxId]; + if (!raw) return ''; + return '../../' + raw.replace(/^moz-extension:/, ''); +} let allPrompts = []; let allExcludedSpecialPrompts = []; // special prompts excluded from UI (hidden + inactive features), preserved on save @@ -182,6 +192,15 @@ function renderListItems(listEl, items, menuType, isActive) { handle.textContent = '\u2630'; li.appendChild(handle); + // Icon slot (context menu only) - between handle and toggle, to keep rows aligned + if (menuType === 'context') { + if (String(prompt.is_special) === '1') { + li.appendChild(buildSpecialIconDisplay(prompt)); + } else { + li.appendChild(buildIconPicker(prompt)); + } + } + // Toggle checkbox const toggle = document.createElement('input'); toggle.type = 'checkbox'; @@ -229,6 +248,136 @@ function renderListItems(listEl, items, menuType, isActive) { }); } +// ==================== Custom icon picker ==================== + +let activeIconPopover = null; + +function closeIconPopover() { + if (activeIconPopover) { + activeIconPopover.remove(); + activeIconPopover = null; + document.removeEventListener('mousedown', onDocMouseDownForPopover, true); + document.removeEventListener('keydown', onDocKeyDownForPopover, true); + } +} + +function onDocMouseDownForPopover(e) { + if (activeIconPopover && !activeIconPopover.contains(e.target) && !e.target.classList.contains('item_icon_preview')) { + closeIconPopover(); + } +} + +function onDocKeyDownForPopover(e) { + if (e.key === 'Escape') closeIconPopover(); +} + +function applyIconToPreview(preview, filename) { + if (filename) { + preview.src = '../../' + customMenuIconsPath + filename; + preview.classList.remove('item_icon_preview_empty'); + } else { + preview.src = '../../' + customMenuIconsPath + 'empty_icon.png'; + preview.classList.add('item_icon_preview_empty'); + } +} + +function buildSpecialIconDisplay(prompt) { + const img = document.createElement('img'); + img.classList.add('item_icon_preview', 'item_icon_preview_special'); + img.alt = ''; + const path = resolveSpecialIconPath(prompt.id); + if (path) { + img.src = path; + } else { + img.src = '../../' + customMenuIconsPath + 'empty_icon.png'; + img.classList.add('item_icon_preview_empty'); + } + return img; +} + +function buildIconPicker(prompt) { + const preview = document.createElement('img'); + preview.classList.add('item_icon_preview'); + preview.alt = ''; + preview.title = browser.i18n.getMessage('menu_order_icon_label'); + applyIconToPreview(preview, prompt.custom_icon || ''); + + preview.addEventListener('click', (e) => { + e.stopPropagation(); + if (activeIconPopover && activeIconPopover.dataset.forId === prompt.id) { + closeIconPopover(); + return; + } + closeIconPopover(); + openIconPopover(preview, prompt); + }); + + return preview; +} + +function openIconPopover(anchorEl, prompt) { + const popover = document.createElement('div'); + popover.classList.add('icon_picker_popover'); + popover.dataset.forId = prompt.id; + + // "None" option + const noneBtn = document.createElement('button'); + noneBtn.type = 'button'; + noneBtn.classList.add('icon_picker_cell', 'icon_picker_cell_none'); + noneBtn.title = browser.i18n.getMessage('menu_order_icon_none'); + const noneImg = document.createElement('img'); + noneImg.src = '../../' + customMenuIconsPath + 'empty_icon.png'; + noneImg.alt = ''; + noneBtn.appendChild(noneImg); + if (!prompt.custom_icon) noneBtn.classList.add('selected'); + noneBtn.addEventListener('click', () => { + prompt.custom_icon = ''; + applyIconToPreview(anchorEl, ''); + markUnsaved(); + closeIconPopover(); + }); + popover.appendChild(noneBtn); + + customMenuIcons.forEach(filename => { + const btn = document.createElement('button'); + btn.type = 'button'; + btn.classList.add('icon_picker_cell'); + btn.title = filename.replace(/\.[^.]+$/, ''); + if (filename === prompt.custom_icon) btn.classList.add('selected'); + + const img = document.createElement('img'); + img.src = '../../' + customMenuIconsPath + filename; + img.alt = ''; + btn.appendChild(img); + + btn.addEventListener('click', () => { + prompt.custom_icon = filename; + applyIconToPreview(anchorEl, filename); + markUnsaved(); + closeIconPopover(); + }); + popover.appendChild(btn); + }); + + document.body.appendChild(popover); + activeIconPopover = popover; + + // Position popover below the anchor + const rect = anchorEl.getBoundingClientRect(); + const popRect = popover.getBoundingClientRect(); + let left = rect.left + window.scrollX; + let top = rect.bottom + window.scrollY + 4; + if (left + popRect.width > window.scrollX + document.documentElement.clientWidth - 8) { + left = window.scrollX + document.documentElement.clientWidth - popRect.width - 8; + } + if (left < window.scrollX + 4) left = window.scrollX + 4; + popover.style.left = left + 'px'; + popover.style.top = top + 'px'; + + document.addEventListener('mousedown', onDocMouseDownForPopover, true); + document.addEventListener('keydown', onDocKeyDownForPopover, true); +} + // ==================== Toggle show_in ==================== function toggleShowIn(prompt, menuType, isOn) { From 2a51e39815af74c6c2e6d535d95dffee044914a9 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 17 Apr 2026 01:29:00 +0200 Subject: [PATCH 26/37] context menu icons folder rearranged. see #680 #184 --- claude-spec/02-prompts.md | 2 +- .../autotags.png} | Bin .../custom}/adventure-game.png | Bin .../custom}/calendar.png | Bin .../custom}/clapboard.png | Bin .../custom}/clock.png | Bin .../custom}/copywriting.png | Bin .../custom}/customer-service.png | Bin .../custom}/deadline.png | Bin .../custom}/empty_icon.png | Bin .../custom}/express-delivery.png | Bin .../custom}/home.png | Bin .../custom}/info.png | Bin .../custom}/invoice.png | Bin .../custom}/justice-scale.png | Bin .../custom}/like.png | Bin .../custom}/love-letter.png | Bin .../custom}/policeman.png | Bin .../custom}/printer.png | Bin .../custom}/puzzle-game.png | Bin .../custom}/scissors.png | Bin .../custom}/send.png | Bin .../spamfilter.png} | Bin .../summarize.png} | Bin .../translate.png} | Bin js/mzta-utils.js | 18 ++++++++++++------ pages/menu_order/mzta-custom-menu-icons.js | 2 +- 27 files changed, 14 insertions(+), 8 deletions(-) rename images/{menu_autotags.png => context_menu/autotags.png} (100%) rename images/{custom_menu => context_menu/custom}/adventure-game.png (100%) rename images/{custom_menu => context_menu/custom}/calendar.png (100%) rename images/{custom_menu => context_menu/custom}/clapboard.png (100%) rename images/{custom_menu => context_menu/custom}/clock.png (100%) rename images/{custom_menu => context_menu/custom}/copywriting.png (100%) rename images/{custom_menu => context_menu/custom}/customer-service.png (100%) rename images/{custom_menu => context_menu/custom}/deadline.png (100%) rename images/{custom_menu => context_menu/custom}/empty_icon.png (100%) rename images/{custom_menu => context_menu/custom}/express-delivery.png (100%) rename images/{custom_menu => context_menu/custom}/home.png (100%) rename images/{custom_menu => context_menu/custom}/info.png (100%) rename images/{custom_menu => context_menu/custom}/invoice.png (100%) rename images/{custom_menu => context_menu/custom}/justice-scale.png (100%) rename images/{custom_menu => context_menu/custom}/like.png (100%) rename images/{custom_menu => context_menu/custom}/love-letter.png (100%) rename images/{custom_menu => context_menu/custom}/policeman.png (100%) rename images/{custom_menu => context_menu/custom}/printer.png (100%) rename images/{custom_menu => context_menu/custom}/puzzle-game.png (100%) rename images/{custom_menu => context_menu/custom}/scissors.png (100%) rename images/{custom_menu => context_menu/custom}/send.png (100%) rename images/{menu_spamfilter.png => context_menu/spamfilter.png} (100%) rename images/{menu_summarize.png => context_menu/summarize.png} (100%) rename images/{menu_translate.png => context_menu/translate.png} (100%) diff --git a/claude-spec/02-prompts.md b/claude-spec/02-prompts.md index d2268831..2745ac3d 100644 --- a/claude-spec/02-prompts.md +++ b/claude-spec/02-prompts.md @@ -33,7 +33,7 @@ Prompts are the core user-facing feature of ThunderAI. Each prompt defines an AI | `position_compose` | number | Sort order for the popup menu in compose view | | `position_context` | number | Sort order for the context menu | | `show_in` | string | `"popup"` = popup only, `"context"` = context menu only, `"both"` = both, `"none"` = hidden from all menus. Default: `"popup"` for default/custom prompts, `"both"` for special prompts | -| `custom_icon` | string | Filename (with extension) of an icon in `images/custom_menu/` used as the context-menu icon. Empty string = no icon. Only used for non-special prompts (special prompts use their hard-coded icons in `specialPromptToContextMenuID`). Selectable from a dropdown on the Menu Order page, context-menu tab. | +| `custom_icon` | string | Filename (with extension) of an icon in `images/context_menu/custom/` used as the context-menu icon. Empty string = no icon. Only used for non-special prompts (special prompts use their hard-coded icons in `specialPromptToContextMenuID`). Selectable from a dropdown on the Menu Order page, context-menu tab. | ### Per-Prompt API Override Properties diff --git a/images/menu_autotags.png b/images/context_menu/autotags.png similarity index 100% rename from images/menu_autotags.png rename to images/context_menu/autotags.png diff --git a/images/custom_menu/adventure-game.png b/images/context_menu/custom/adventure-game.png similarity index 100% rename from images/custom_menu/adventure-game.png rename to images/context_menu/custom/adventure-game.png diff --git a/images/custom_menu/calendar.png b/images/context_menu/custom/calendar.png similarity index 100% rename from images/custom_menu/calendar.png rename to images/context_menu/custom/calendar.png diff --git a/images/custom_menu/clapboard.png b/images/context_menu/custom/clapboard.png similarity index 100% rename from images/custom_menu/clapboard.png rename to images/context_menu/custom/clapboard.png diff --git a/images/custom_menu/clock.png b/images/context_menu/custom/clock.png similarity index 100% rename from images/custom_menu/clock.png rename to images/context_menu/custom/clock.png diff --git a/images/custom_menu/copywriting.png b/images/context_menu/custom/copywriting.png similarity index 100% rename from images/custom_menu/copywriting.png rename to images/context_menu/custom/copywriting.png diff --git a/images/custom_menu/customer-service.png b/images/context_menu/custom/customer-service.png similarity index 100% rename from images/custom_menu/customer-service.png rename to images/context_menu/custom/customer-service.png diff --git a/images/custom_menu/deadline.png b/images/context_menu/custom/deadline.png similarity index 100% rename from images/custom_menu/deadline.png rename to images/context_menu/custom/deadline.png diff --git a/images/custom_menu/empty_icon.png b/images/context_menu/custom/empty_icon.png similarity index 100% rename from images/custom_menu/empty_icon.png rename to images/context_menu/custom/empty_icon.png diff --git a/images/custom_menu/express-delivery.png b/images/context_menu/custom/express-delivery.png similarity index 100% rename from images/custom_menu/express-delivery.png rename to images/context_menu/custom/express-delivery.png diff --git a/images/custom_menu/home.png b/images/context_menu/custom/home.png similarity index 100% rename from images/custom_menu/home.png rename to images/context_menu/custom/home.png diff --git a/images/custom_menu/info.png b/images/context_menu/custom/info.png similarity index 100% rename from images/custom_menu/info.png rename to images/context_menu/custom/info.png diff --git a/images/custom_menu/invoice.png b/images/context_menu/custom/invoice.png similarity index 100% rename from images/custom_menu/invoice.png rename to images/context_menu/custom/invoice.png diff --git a/images/custom_menu/justice-scale.png b/images/context_menu/custom/justice-scale.png similarity index 100% rename from images/custom_menu/justice-scale.png rename to images/context_menu/custom/justice-scale.png diff --git a/images/custom_menu/like.png b/images/context_menu/custom/like.png similarity index 100% rename from images/custom_menu/like.png rename to images/context_menu/custom/like.png diff --git a/images/custom_menu/love-letter.png b/images/context_menu/custom/love-letter.png similarity index 100% rename from images/custom_menu/love-letter.png rename to images/context_menu/custom/love-letter.png diff --git a/images/custom_menu/policeman.png b/images/context_menu/custom/policeman.png similarity index 100% rename from images/custom_menu/policeman.png rename to images/context_menu/custom/policeman.png diff --git a/images/custom_menu/printer.png b/images/context_menu/custom/printer.png similarity index 100% rename from images/custom_menu/printer.png rename to images/context_menu/custom/printer.png diff --git a/images/custom_menu/puzzle-game.png b/images/context_menu/custom/puzzle-game.png similarity index 100% rename from images/custom_menu/puzzle-game.png rename to images/context_menu/custom/puzzle-game.png diff --git a/images/custom_menu/scissors.png b/images/context_menu/custom/scissors.png similarity index 100% rename from images/custom_menu/scissors.png rename to images/context_menu/custom/scissors.png diff --git a/images/custom_menu/send.png b/images/context_menu/custom/send.png similarity index 100% rename from images/custom_menu/send.png rename to images/context_menu/custom/send.png diff --git a/images/menu_spamfilter.png b/images/context_menu/spamfilter.png similarity index 100% rename from images/menu_spamfilter.png rename to images/context_menu/spamfilter.png diff --git a/images/menu_summarize.png b/images/context_menu/summarize.png similarity index 100% rename from images/menu_summarize.png rename to images/context_menu/summarize.png diff --git a/images/menu_translate.png b/images/context_menu/translate.png similarity index 100% rename from images/menu_translate.png rename to images/context_menu/translate.png diff --git a/js/mzta-utils.js b/js/mzta-utils.js index ffa24ca3..403a475f 100644 --- a/js/mzta-utils.js +++ b/js/mzta-utils.js @@ -16,7 +16,13 @@ * along with this program. If not, see . */ -import { prefs_default, getDynamicSettingValue } from '../options/mzta-options-default.js'; +import { + prefs_default, + getDynamicSettingValue +} from '../options/mzta-options-default.js'; + +import { customMenuIconsPath } from '../pages/menu_order/mzta-custom-menu-icons.js' + const sparks_min = '1.2.0'; // Minimum version of ThunderAI-Sparks required for the add-on to work export const ChatGPTWeb_models = ['gpt-5','gpt-5-instant','gpt-5-t-mini','gpt-5-thinking']; // List of models available in ChatGPT Web const MICZ_IT_LOCALIZED_LANGS = ['es', 'de', 'fr', 'it']; @@ -29,10 +35,10 @@ export const contextMenuID_Spamfilter = 'mzta-spamfilter'; export const contextMenuID_Summarize = 'mzta-summarize'; export const contextMenuID_Translate = 'mzta-translate'; export const contextMenuIconsPath = { - [contextMenuID_AddTags]: 'moz-extension:images/menu_autotags.png', - [contextMenuID_Spamfilter]: 'moz-extension:images/menu_spamfilter.png', - [contextMenuID_Summarize]: 'moz-extension:images/menu_summarize.png', - [contextMenuID_Translate]: 'moz-extension:images/menu_translate.png', + [contextMenuID_AddTags]: 'moz-extension:images/context_menu/autotags.png', + [contextMenuID_Spamfilter]: 'moz-extension:images/context_menu/spamfilter.png', + [contextMenuID_Summarize]: 'moz-extension:images/context_menu/summarize.png', + [contextMenuID_Translate]: 'moz-extension:images/context_menu/translate.png', }; // Map from special prompt IDs to context menu IDs @@ -59,7 +65,7 @@ export function getContextMenuIcon(prompt) { } if (typeof prompt === 'object' && prompt !== null && prompt.custom_icon) { - return 'moz-extension:images/custom_menu/' + prompt.custom_icon; + return 'moz-extension:' + customMenuIconsPath + prompt.custom_icon; } return defaultContextMenuIcon; diff --git a/pages/menu_order/mzta-custom-menu-icons.js b/pages/menu_order/mzta-custom-menu-icons.js index df4ffba3..7c8067a0 100644 --- a/pages/menu_order/mzta-custom-menu-icons.js +++ b/pages/menu_order/mzta-custom-menu-icons.js @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -export const customMenuIconsPath = 'images/custom_menu/'; +export const customMenuIconsPath = 'images/context_menu/custom/'; export const customMenuIcons = [ 'adventure-game.png', From 3240177717e8f1400de4cd882c29efb772d01641 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 17 Apr 2026 01:35:00 +0200 Subject: [PATCH 27/37] prompt import / export fixed. see #680 --- js/mzta-prompts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/mzta-prompts.js b/js/mzta-prompts.js index f81f9803..02c52545 100644 --- a/js/mzta-prompts.js +++ b/js/mzta-prompts.js @@ -488,7 +488,7 @@ export function preparePromptsForExport(prompts, include_api_settings = false){ } if(prompt.is_default == 1){ - let allowedKeys = ['id', 'enabled', 'position_compose', 'position_display', 'position_context', 'need_custom_text', 'show_in']; + let allowedKeys = ['id', 'enabled', 'position_compose', 'position_display', 'position_context', 'need_custom_text', 'show_in', 'custom_icon']; if(include_api_settings){ allowedKeys.push('api_type'); for (const [integration, options] of Object.entries(integration_options_config)) { From f903149e516f917288dceb566330b795a6371eac Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 17 Apr 2026 01:38:00 +0200 Subject: [PATCH 28/37] more context menu icons added. see #680 #184 --- images/context_menu/custom/airport.png | Bin 0 -> 994 bytes images/context_menu/custom/alert-sign.png | Bin 0 -> 974 bytes images/context_menu/custom/apple.png | Bin 0 -> 857 bytes images/context_menu/custom/cash.png | Bin 0 -> 971 bytes images/context_menu/custom/coffee.png | Bin 0 -> 987 bytes images/context_menu/custom/coins.png | Bin 0 -> 1258 bytes images/context_menu/custom/discount.png | Bin 0 -> 1085 bytes images/context_menu/custom/earth.png | Bin 0 -> 1529 bytes images/context_menu/custom/electric-car.png | Bin 0 -> 1220 bytes images/context_menu/custom/experiment.png | Bin 0 -> 1308 bytes images/context_menu/custom/flammable.png | Bin 0 -> 1031 bytes .../context_menu/custom/radiation-hazard.png | Bin 0 -> 1568 bytes images/context_menu/custom/receipt.png | Bin 0 -> 838 bytes images/context_menu/custom/recycle-sign.png | Bin 0 -> 1262 bytes images/context_menu/custom/star.png | Bin 0 -> 925 bytes images/context_menu/custom/statistics.png | Bin 0 -> 1083 bytes images/context_menu/custom/sticky-notes.png | Bin 0 -> 604 bytes images/context_menu/custom/target.png | Bin 0 -> 1849 bytes images/context_menu/custom/thermometer.png | Bin 0 -> 795 bytes images/context_menu/custom/trophy.png | Bin 0 -> 990 bytes pages/menu_order/mzta-custom-menu-icons.js | 20 ++++++++++++++++++ 21 files changed, 20 insertions(+) create mode 100644 images/context_menu/custom/airport.png create mode 100644 images/context_menu/custom/alert-sign.png create mode 100644 images/context_menu/custom/apple.png create mode 100644 images/context_menu/custom/cash.png create mode 100644 images/context_menu/custom/coffee.png create mode 100644 images/context_menu/custom/coins.png create mode 100644 images/context_menu/custom/discount.png create mode 100644 images/context_menu/custom/earth.png create mode 100644 images/context_menu/custom/electric-car.png create mode 100644 images/context_menu/custom/experiment.png create mode 100644 images/context_menu/custom/flammable.png create mode 100644 images/context_menu/custom/radiation-hazard.png create mode 100644 images/context_menu/custom/receipt.png create mode 100644 images/context_menu/custom/recycle-sign.png create mode 100644 images/context_menu/custom/star.png create mode 100644 images/context_menu/custom/statistics.png create mode 100644 images/context_menu/custom/sticky-notes.png create mode 100644 images/context_menu/custom/target.png create mode 100644 images/context_menu/custom/thermometer.png create mode 100644 images/context_menu/custom/trophy.png diff --git a/images/context_menu/custom/airport.png b/images/context_menu/custom/airport.png new file mode 100644 index 0000000000000000000000000000000000000000..9fb2dc7fd027af0cd198ac069ae58f1d81e01726 GIT binary patch literal 994 zcmV<810DQ{P)Vt(|%AxM-)&pL^dt5{xeR zR&&q!?)QB;@6LS>Zs9+M{A7zfh*8|xwd}tCdahu51aP?JH0rED^%xLX zc~!Mh&8KjJ?fK>q^&apBni1y&u&$t9Sp`e$qsaUWx|L+(H^q(U7g5jE7-N(BK;8hV ziQNWuD?sc+T)?+T#a8yL95pp>{3bkd;XdT{52(AR8P|S~&8y3KoIiIIk3An{6bx9p z{#Rl%cb4VNq#>R9ki(m;<`8xWO*pGAvFX#mdOJyBDh25jpa;rH-dKRi;kQAavU>+G z@f%F17Yfou=cPw(PJoj`2Y~(7e&E9&zrbJ9#i3pflZ|!=dxoAwI0v|9f_}&NXqxO? zv!UtZu!e)1ubFn1pTMc%2N6E9o67}ev(03(&jGUrf{SmTT;T+IuEwBlKO(D9{TiHW zINFC&Z`psGCGi(Yhz~%=rGucI2HthlS%tT0vba(BHfwMCh{zkXDkbu2}nv$h->tZfKO;yoCW?xOaj* z4GECPQ5h-O?gy*On*-hx6NX!GfCo~ag$KwX8NZ0|d`Vs8p-2H>{(C~?CD5;cZggfF ziG*Jw87gvW=q$>PJbF9H_&bq9L_ql4P~5#}b_vKv;0!u`{AQZp!v6w)1FkRwGS-Oi QbN~PV07*qoM6N<$f^1dQRR910 literal 0 HcmV?d00001 diff --git a/images/context_menu/custom/alert-sign.png b/images/context_menu/custom/alert-sign.png new file mode 100644 index 0000000000000000000000000000000000000000..e1a0430ecdde3ea470374e08d6e694d8425c2da6 GIT binary patch literal 974 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 UjKE8VegFUf07*qoM6N<$f(8Xa0RR91 literal 0 HcmV?d00001 diff --git a/images/context_menu/custom/discount.png b/images/context_menu/custom/discount.png new file mode 100644 index 0000000000000000000000000000000000000000..9fa4cf236510c416e737c665397443383563177d GIT binary patch literal 1085 zcmV-D1j74?P)4_%8Zf{E)5VvB zha3$MFxR$1Svh#)Q(%ugY8#STSO@ZWtveYWE^2@Pu1mSr4(>T{1Y-9dc-p=NYglRu zNi=W7*xoN8=bK)28b;Vi1uHMlz))TALg6~k2G%!#1p-KIc>xsb2Y=?-eE4%4@b}BF zqWsulh`#HV1_)TK!b^5p8W;}M?iMrfTK znAo`kK>8#6XUkT|ssR4_@H5CF`vd?Y##%BLnCvR>NBiEG>GhgUD9IsJ;2e+i?84kG~?a;Q?WG)8|o8qk1& z=`sL?-yIFiiN@MJjLT`j4%5 zO1_peeiJ`-{^IIdUd+^Qi-X_aP3liu8=(C>(zC-3W+k_OG3v3av6ZuYzE+lRT_<8M zX_21oZs3zOOVjA01?!VgyXRFrc##|OP^mws_g()2@sNYC;DPJ#00000NkvXXu0mjf D-!lB} literal 0 HcmV?d00001 diff --git a/images/context_menu/custom/earth.png b/images/context_menu/custom/earth.png new file mode 100644 index 0000000000000000000000000000000000000000..a78602078b519f882af403491aaaffd6a3f6b0dd GIT binary patch literal 1529 zcmVo0+XI8XP^IdlHMnK^f6F8q&8HWjTZM}IMNpL6b* zKpJGbLczdIRVIwc-`+c&?ma_8hr* z{=2pLR#?~hm!2$ogXirXDv1_b6)yR$;5;PXj5l%|+{zdLN7Vi<(T?j-uE!{$-c;9D zd%yJ5A6Er9`1-lFfDb=%X5LTT=^=LQ8ubTGAlcTc_Hgf?aLpf9kG9wtFm-6}wa30O zT5gLiL7)?FMUwSxy{s#4GHE2&Qq``>xtO>V8z87F`4U-Q>LxD4pvn*X&%Js)s_*-Y z;z;iFuuZj#&CKn=-n<`~-?<8!677BuO=PhX2a()P5XH4zBiiyYqT>9_fy4j!`WO2F zyb4Kj`W3~MMCz+YZ{}ODBgfVdUK*1fy?+IhU?z@WCXN6YRGtQYP#;!iZN$U#AMQtNtYT#3HQH&YZyoJ+cD$!Vl!!QUM#6B zY~3(`XlaWy0LJpka%WgvHtn`Z)OH!yFkXVIyG^wBZNlAuuS#mB(-jEGwj%S@8&$=k zLj9?_dkK!JQCI~&fFhx4Y#TR z)DBl>KqPfJmOKgw7v>jVY8^I!2?*Q%N^tl!GaJNO3NM z1ohbAhmk0`s)*!vtnSNi!(=*F#`huJZV3P|k#-{z`;Nl=EJ1D-Gu?rI{mH7Thf?L6 zT=S{`0A}*8rI*OVWw$?5@s%I?x*r+Bm|w5#T%5a2kee+XsbM1Sj1t)4FRUpJAguPHh5lljH@Q%oa9jC5->-(|2y6V=^ zf9BXV>!+Tm1tFPMinl@*ZUh8lDKzg;b=9ISB+O?iOplYBx=E0mo%1K}`{wmi&s?a= zxf=QY7v4E!{NPPhcjpprNZ57-P0e6t+OgyJl#&>Wh*=bmVo)OTvBjO}JM(ByEuA$l ze*1s=cC$q0XSnd&K%xxc>L)StEud@sBZw3P`K9%f&-MNASZ39*wE)U?;Osk}F+|U+ zx<^&rn%5!`CPciqh~#X4_vZ)K%f0EJ)4R{U`yjUPFpf?|w~DbUqSMBi!N~FF_s=|Z fg~c-czp#G;atJ{jQFMa|00000NkvXXu0mjf`25{I literal 0 HcmV?d00001 diff --git a/images/context_menu/custom/electric-car.png b/images/context_menu/custom/electric-car.png new file mode 100644 index 0000000000000000000000000000000000000000..7ce1265fa77966b53c5dfc825d63c5c7eec4b838 GIT binary patch literal 1220 zcmV;#1UvhQP)j2ssgX2`( zoCHE9C4#}~j*j66u!zd0P4yFOX2q*&v96q0OxD;e02;f`v-$eBe4*Epfsar)%xyQy zuD^e>KX;u@rmS1sGIz)2$nn5#tbGwM{;G&01O`A3$A5Oy#%Db1&3#QR(P5E|5<@S` z(SF{_SuMuPOeql?qDKO2?wSKc?wJoSfUS|*=nv+Ju$?T%4<|1GS}0bIo9j#>^@@>p zXRlIwAKjxiDr@R^&XuxwjjR~)z^?F@Q+o3qf1ibdj=ryV*>>`&%usjht~?uET&qiO z{-CC`0w^mhH8NNvCf;wG<2Y7nWdR~?x4e+NrHVzarx2u%Nx!eFiZ{q-yZ5W!hEb88 zxFp9lmzi6qgcUb>0@xb4z5d7_AsZ>Jm-+mtri}Dw2T)+6e3Sf+bn6Z!JE5Vc&AavN z(&26{n-j>hDQu3!tIiluSJv`&^ayFj#R5nWX?-;@G!5jxsR4wM>kxLh{HpGoww1hUiMj)f^2^V@pz0qd+SiKn+OGm&*W0u zn*TuSS|hdCDga9A*5wJ*Q~)pGY5no_gVTya>1B@<5{o6sNcT~9xY2Zx{DlpSjd^dOP)Sd>WXQidUvh5aZiB>Ga4OsohDB3mM` zk~EY>h%)iV8kz-){SZX^u*g3ug#O4a2UmA?W}J_md*^v_)mzW1C9++o==CYHWBVm}qspVkW?gdmv-XKTiAw#Q;ZpKRM46R!*4tVE^bYcHDL z2FWMaOCX!~9ufqfK>(WWJQ9nE)t#Nh1OV>&PslQb>-q4Q7zmQD{t9rvyZlBT({$(f z%}S&V=U6mu*r}Z3X)@gwBp;~~s-Apx5r`)2H?tPo z+tDl$O|C4Q01%&Gaicrj`!FG1Nxyvk8?NuKOxxD6&T|J3*2){G1sTIy*&HIf!U`oW z7Zo6&z9{@m~%H{&L_H%Eei%Q=|vrG^L$C;8?R8SjO$l#NT03QZ{&Y&>lt zYq;p2E&sN-OMkRFw)L<5KaV~5;y&j411*X&?LbaD1D{-Rc4SofeMTre?n9AkgKt|D znF3yq)FklTIDhZRsQIO)O6X}1SJ-X^p3CJr7}Nm*luDj5*%DCs!om*ia3BH)68xPD zjO;QzAGX8M8c_p*13)I{ToSU#7B5GcczZN$fmIzcv6O`g)5Y?3 z^>ol_PvjVq1`py20FeK4#n7zQ5cIGDSt>VdGWbUe!;c3|IDfV}v(qG@DM2Ctpeg0U zs!?#DP=T}=yaKe12y?RHcEE4k$oE$32myTG@0pgsJ+i+=;zBqs$F*c0hUH;kmU^;Y zZ~&mHw0=&rj5fVoO;sXr8J@X*U4(1J5CGUcVyqiwbUP&weTx6VrUj9jSLdxHn{}7@ zRQw8{Vh9X}BWZAiReJWmZhcuz0=Fyn7e6hc$!ixtsa|ap01_83gP8`TC5wOQ+qq@M z9VH+J2Pqp1U6cPF>733d=Q_BapMXRuv^7t@HSg06dvDt6{wJ&!=5-w{fzN6#tSV_% zHH0J?-1osxoF0k|9_Y9u1gy`8Uz5(9Ukv~VYd~A1u$r_fzU@tg?l`7N-1Fy0ZkvF$ z=ja0U&vRcBuXt}b9^kU#D*iGxhk=f}08N1|s|d@5`ON?GZ)9}Sjx#`mGZ3Wj2q{2M zd*PXJAR33w8KSu~06?M?P0u~Ov7rh$^zNZsf&-NovnafTBpDH<&>rsl;HSMVNgGd}Yq&iW)5w0`8o;cqZayp)TfcAEO+_iPxss8~Kqc=44 SVwQ0L0000!8WP!PHi}8VJc+nV* z`ec0If%#zE8WzBnOsCl)oUT^PbJTE=> z{7?VC-}&F(-g5*#o7|)}AjBa+BITQ6G}`AUkMIQ#t4$z2&sA3i07CJ^O@wR{!_lLD z@_B~KaAY}iFeb02v+`;>D{pjsDP=g4mz&i+Uh{dX!0nZ1>z)hT*jI0em39wO+8g&= z3D*KL9BBl4iowkiw2I-bV{Y;)e&L#?@z%Q^v2^{qJ}=W+L#C;^1WC8t9NGQ92w1KB zw+ojTo0#I0PmdVpdzG*iuq*p_-;WeNhdFz0+P8$IfDW%U!;{Gcz8yNLU-y3#H+?_T zT0^FBo1;Wg0R#LsBV0^T&sm$0zj)IKcsq z_YHDwX{l%oU@?`}`=`%L7M)Y95{e9~bwJDx4shz!glX)B>aD1+?lg_h%+6ZoF-U~f zx&VldTy<569UNd`F=e=Js(+D4+b(jsW%^IVNhVkBdn#>RsoN6D-`7e)Ly1EQ2+ekO zq1|`y-Fm-r-5T~5RvrokdH?*I6)_qeK+0F{`1*%sJBj+*?F54X04mlz zYKm`rehUDhV33B|?Y8h+Z%ol5Mxz4?fEbPTA>}SRiHBD|$btPTLI~0+^F2w;+vW!DDBxGZE!>rWR|zk0*8*-Oyuv*RI7#?{`)YEN+F*G!3rCurGZ>q{ z!)c)shQq1PmHQF^kX!ccK%)Zyg?8~%XTP63{{X^%E$i3IZ{`31002ovPDHLkV1llg B?fd`$ literal 0 HcmV?d00001 diff --git a/images/context_menu/custom/radiation-hazard.png b/images/context_menu/custom/radiation-hazard.png new file mode 100644 index 0000000000000000000000000000000000000000..1e151f1f47eeae36acd7d23820f4825b53293b07 GIT binary patch literal 1568 zcmV+*2H*LKP))qMeot=+|S$n%-keLoa&e9#fPxfV}uN#0G-Zel){RLgWna(aqr8gfi1-LFu zehMoqC1TDGQ6EXa<5Qgyl-yB~4R}7MA z=fEw=a1NkYPU7+inzGU*^NvCe}ljkhXTsB%dKXcjOPRNRE529-Gcs;BtdES_T+B4Dma7Xo$1fy71h#Ae zzj&)Uf?i|bmHw{*Um}qJ;O+O@XZ)d2hkUV?zxkqYXw+$A0dK!&foVh~9flg2uDvw? z$X#d}SQdqnaQ3s-aR%6$4v}>Pi59K3le3>2c*=xI1?(6EE>{sRX z8%u0SDGrRpnRPX0U5%$k;%rTa+`g;a);8-_A1n(9I{+fC6+YEd-!V{T{HevZ8?}<( z{b;4V!skn{*2k0vs|<)tQwgri*)Mk&*p?3I=?MA7?!vVV{8AKuh;B`^9IP?`nYE9b zl&^oe%<-QsZ{DDguRVJnSms0xK&kKvrV}Bv8*6-{EartD9!%4}EdqUcR%iGTm###M z>{SX@s^KwADEVO9Z^CK__RYLY(VN5D1eHX6VAj<*eaYnXC6ifKpDzd1;;tHeps#VI^FU6SXn7?;XDNOApElM{9oJmdw|~`cPM%l$Yda2 z&3ut443(fn!r8N4zYc)x%T8lCdzqhCT)GIk9IFlLt@kaam)g&U3b>jLIPiKDiE8A8QCFWj(I$Q6Z|WsUWr4#NsT zP$F&Sx%(!UpWpRwOkG8^FD=1*R*_5&%46zP_K+Bj{wh_C~{m>O+N{gi1p+7hRh`*ZwGvHpcn?TD|~z`$_TH S2sZfu0000$KpgmS3hg%&}OMi4@Yt(z`fA4EB#G>uXbEXo8c%tB(iV(GB8*{kpArfhAJ@B41koBmga=RD{6|IXo@ z!vn0bXu05|Yr2^i78?Pm)oRtaN}==sU=i5(2*S0Lq@*V+0=##*N*4HqrvRWOEqhWeJXsI4qRMQIU;mVH>!1Mv9+ zXt7^{DcOJ%M-D)(R$}Po7+O0V$jmSyKYM*_0Y=|WV8%O#EA|!`^@#w0qAmG2w6_Xc zjY?`c>6b9gWCX`?xcjglKX`sAF2`IJz`8UOnj7liaQ0$f(;2jPI^p)r$TA0HUP4=$ z1?IwB-0vBHvu_X%XD=G}*Pv?WcFDD5{{yK;JsRq4xY2qNdn$L}Vuu5hQ|{FP@c9Ev zW0g{gs`6q8jA6>{kz7m0EBJt8_BK>lEwF6O$NW5x&aTHWBqpFBCoA><8#iR4slFCB zyZUgiyB{18Hs@vI-0?=}w3^rh5D{!v3v5;kPG4vTafDMx>m?h@1_)Mpi5awfPqR7z zCFa72%q0PQnfVHTU|v*iyjFu`eNuD*21iCP=ktlmP0;C3RcehcK()w%QY9MT#hWqs z{C-inI;|Gla`QyxMzQPsBPmUZ=o1jTBdLnX3H%3$6d({Zen$*LA@gnkw*l}$G5%NV zuZDmahJx1 za2Nmt4wp@O{e;WqidSfL2N`1p#OO|nG4TNaxV_$8oPeUhx4B3EI*m2aPeiEJW1G+F Qp8x;=07*qoM6N<$f`Jium;e9( literal 0 HcmV?d00001 diff --git a/images/context_menu/custom/recycle-sign.png b/images/context_menu/custom/recycle-sign.png new file mode 100644 index 0000000000000000000000000000000000000000..d8094df36b50b91720f713c7f9c99dfe647409f0 GIT binary patch literal 1262 zcmVycFxuHl;%((L{+F zB~~;s0uO48RUS+TmYXCJQ+)slltPFX&=NG^0f>nKTIC6h7eLwqQwbIj#Ro`CMA|ZK zr*qC;K8zh_&P+Q~lqc51Bxhaz|6lv8z0V%F)lF`sDDXqm6x*+_q687k0LSv3=&s{) zxAot20o7eA>P6lCAk#geUybO~KhQAYPy=fLYePLnF=5(d&k4iI0L0!7f*<7c{_;YN#3&22UTl1;G`)Z zHo5+B1!TPoDi1V1htZ9o_t>_z?rcNbT7bIlrBjWucPsKl8t{FmgRKk4!%hDRwArfe z?5hB(#Ug-GL5{sPRI{nF>d=zXvrp}~GMehTG7XV(zy<7N1@d{O#*$WG=69=$a`Q$y zK>RXwf97OGxGLZx1if?fuIe<~_ z$`9LXy}3yOYEL!YA@Eij%t0WcqKmQ-;5TMJA}QyzG3L#CJ6aZ9e3GQ|4YU zPIs!l%vQg|?+phKIES*|A7@pR*UK!kmO-EZ6s%=PNBJmbKF%tXUj!~R9KbDdKw$*S zktlBrdx4G!)oz46F|lP(VZ<$Rpy2=_sAt&(?3?BM5-Sk4B%A5;$M|aYrwY40L$3&0 z)1y{#E>sayW(+7+Y56N(WObHl6m>npewpuUm;-$6m-w!pnN@c{hw(!ARt!CMA?;eA z3s}MYLf=HVjB-URSKMX%Bg^sU6(AU9qch2G;Lw zw#)$ejcm8=oJb4M)Gi3yTGnzZNmqeA%HJ}<$|$FcAn2;!TZR2CMSe=pRZXNgc7W^7 z6F!dx>wxf+I^2ch`88}3!KRfHe}SH-MK7^f1nYG2rM6Ou2`Z4g1?l4wDCL*1Sp=)2 zEY)lAw<{LFjPi{?#-LfqUE^^EK-Mv-IsQ$ED&XFnc{?(2b@l)<4j0Wm?)qa4nlkGX z0aWNm_!QWgx37*SIFXv<}!5(5lrfARV zLqI*i1Z)3Kcp{U7KxKE0mFjK9smWI2Bssc%Ck62R`X&}#gT9xA*(7G(c07~T~mM18RdeL^w|M!*h4%FkF%>O^DOu!hWrvo zo0-w@0|p>Mz8O8#`AzsH(9{lCIQF`Nyfr)rr8os5T(1o_00000NkvXXu0mjf?kA%} literal 0 HcmV?d00001 diff --git a/images/context_menu/custom/statistics.png b/images/context_menu/custom/statistics.png new file mode 100644 index 0000000000000000000000000000000000000000..7e3f1c3f1cdf6dfecd11b7c211e90449b8c90b3b GIT binary patch literal 1083 zcmV-B1jPG^P)PBxS-N|7>oY!#!2BO^di(mwW-^pl1Za<*CBC}C(0GiI ziE(O=AIB!kjNZPDzpRY*mR623jFjjjB)}5K(1{*gAf3r@^~SF(udLv7I5^*SjuW8} z0FlrWoT{#2ZfTKQQE(k7^pB44dn^V(L)}SQ zo0|AM{u5sfd;x%6lKD3B4$;a8k#HDI*Ks+We>KR3?|j~`z{KPvs`eq0{AYTP;FJ#l4R2z%;FRDkJsfE~SSY@+S~1Xwu2NKH2~P002ovPDHLkV1l|P B2qXXi literal 0 HcmV?d00001 diff --git a/images/context_menu/custom/sticky-notes.png b/images/context_menu/custom/sticky-notes.png new file mode 100644 index 0000000000000000000000000000000000000000..f04554fb3096f5a1c702f5b77a6228e69af2f6ec GIT binary patch literal 604 zcmV-i0;BzjP)L}ZG5|yFYlMNee$tStC_kaSHj9x37hp&7 zeJB-SDE&;PKi`OIF)ikEr?Ld)2VI-*RH=Af=g&QF3!sn2@?aNG#uGCSAqu%Dh!8*@ zkBy?-BgvhcrctFrPUp|g?JRlW>W`g3%)woUeRv1uvct^4#CaGVuGdBmSD)6_YhrHd zi;H>5z4aRb#H4qh4xBy%LB#t6we=fYq<=5JqWm5T-691kh&CUe(J$F9W>{^fC~g0kx6k#3U5m z0F{_$9Zwa+TX6#vHX#W9g}EYnS1!HuYlV6StXad@S{wXYgLI~vWJ%z*ihLwBa9b`f zEjN@yw5P`h2>Mom6WzPDNU>azTi=(PUxZtenV(xIGr*68%`2dHqRb)U4_F_bOd$s* qG4@#3)@qB0w<5FO{PxZJReu3+gO{C!6c}g#0000Wp=umWoa%;}nY4YFdygbvm6gXxIYu1NTi(if7Tf7AW}|_hz)-BR%tFaqL(lfP07< zrKkslr5Gs|kh~)SEfp0Lq^WikL?b^CLWJ)`Y?kaKh~Mjl;3mKT0ZkRX4_jad%FPh>I(@}Af;3LTu)h22=c7-?t9ifMRMt~fm}_E$g3^s9gQu)8#ms!X)DAZ zOK0=i;YN(N9$=}Ju7I=A3 zQF^M|ab^M(L?hfaX%ZcEyU@Lr5Z&G6{rZ=fC;mHR2LN`KR$HslLdJhY%UnMLBU z$NPDTrMh?X5?~b0YENs~XEt(k)BpBfME36+K(0d}oar-hrq4u&LhOzf@^Y-0r{gWG z`{Fp^4?pmI?eEV=$70#xCCsU+@&$N8J(O+r?*C5Us73zvZh+jGHl15_yXoAzozAV> zsay55PyR$Aecqz=Z)e0>A)M;_1346GZbpD7CIwM_F2PDGHw%H@|@$W#_yexjq7=shJh+93I<$kz-Vv7E4-U|b7uMLM%z`)ix&dE zs%WhLEw5h-G7E7FdRt&faU~FVp@Ecb1K^Fn2XAa~CeF6WeSR}Wds~1!_l3;}$M)B} z;^6IQUbz$$p!G$+0;ZPxfu(&m`<=I8aV=gbjA2NUk1i)?`_@ddhI5R3a+Tk6^Fl*_ z93b)I6&cZvjrG~50lSp?pkIO3Xv3cjA^ZmF))tIw?ZlJm!6muyVVwJB2NDR!#yWc% z>+I)*J zLv;ZVa4J34@0zV@L+7a(AgmqOnm+Sa0s0>ezXn!YD?1NuZ+-H|0W2I9VYykFe-S1= z^jA=Asb?=qJ$w1?KRYY z2s_j3qQuvK2nU9{QPN>F0BW3pdfI=h1MNT%MhhCE1CDCi8VSN^K|^N18VO`^;m(1! zHvf+U7`P@%ul1TYg^B-9bs)k%@bBw@F@o}SLus522wV8c+PNg@5R{}jO3`Wybp_eU zslBS42EbNPs3HP!*BtxsDSxsd0IF(GxdOPTlDOVC=W{OKm^lbe*>X`;BhS>lFN{qF z8x+<9#O^&Nar5q+&%OPC(xvOO5VHJz4eR;*#Hy?33O;@Hjms(o1J7r}6f3&9?f@cA zZmwWzSrr#}ei3(N3V`CYLYa?s2k`UmV;~1Gb#ND6OM*;cl6RAzv56S5b0;VSmXbxs z6UiG<0Yd#7j*k21n+z8O&|n(dcOQwzFkQRxfzgRFahJR6(hK)B1VB}tx1)uJao22% zy9O~zsjTFEOU}0}XfkixS0p<`q|^|ASqOQPDR%qQ_ISChixhAj8Be&TXKgBZDr(`1 ZeE|%47o+X3IKuz{002ovPDHLkV1jJ9WF7zj literal 0 HcmV?d00001 diff --git a/images/context_menu/custom/trophy.png b/images/context_menu/custom/trophy.png new file mode 100644 index 0000000000000000000000000000000000000000..937269a93ac8192ca65e4cfe832b48d3b01dedf0 GIT binary patch literal 990 zcmV<410np0P)VIS%JCyCDeHnwlt2rcnGwVFt+BooKaKrkFZWbVHd&L zw+Rb7kh9_=r$AWPL9pg6g2K*LEb9P_phoE?HM@)NV_`Yf%)-;SiytHxRapd_zGl<3K;gm%t5(?v)Xp1Z!T$DkpIkUaMmCK8GA$A#bwHSL&5wAEd@7W-e>1-LK z8HN?{ImEW2xfadg4jW)%wZ=?8A{$`BjLpFD)tfnHFp?!aGXQ`MGG$#1n6aDi{Id)| z$j@dxV-5pWVmh;L*2cK}kJ%rdVYk(85(VHE*xzwCds_8s=`H}!w{+t%YEJdU$p>>l zE~7d^zE!`>E1)Uv5$wdc8Bt7ZGRlar4PLbppR^6I=PPwdm1{S3e6;qi+>0NAI0`TmldYAlQ0_>V>p~;f;gIsU;z%6zrsh;``{N)Ej z?S=!G*+w@EhSKU@TH&%@@9^&n;Zw+x_d7Fz%I7-0!) Date: Fri, 17 Apr 2026 01:44:00 +0200 Subject: [PATCH 29/37] empty_icon fixed for dark mode. see #680 #184 --- pages/menu_order/mzta-menu-order.css | 8 ++++++++ pages/menu_order/mzta-menu-order.js | 11 +++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/pages/menu_order/mzta-menu-order.css b/pages/menu_order/mzta-menu-order.css index 81b5055f..2fb990f5 100644 --- a/pages/menu_order/mzta-menu-order.css +++ b/pages/menu_order/mzta-menu-order.css @@ -383,4 +383,12 @@ a:visited { color: #409eff; } a:hover { color: #66b1ff; } a:active { color: #66b1ff; } + + .item_icon_preview_empty { + filter: invert(1); + } + + .icon_picker_cell_none img { + filter: invert(1); + } } diff --git a/pages/menu_order/mzta-menu-order.js b/pages/menu_order/mzta-menu-order.js index f2551e54..9e02d7dc 100644 --- a/pages/menu_order/mzta-menu-order.js +++ b/pages/menu_order/mzta-menu-order.js @@ -23,8 +23,15 @@ import { setSpecialPrompts, getHiddenSpecialPromptIds } from '../../js/mzta-prompts.js'; -import { i18nConditionalGet, specialPromptToContextMenuID, contextMenuIconsPath } from '../../js/mzta-utils.js'; -import { customMenuIcons, customMenuIconsPath } from './mzta-custom-menu-icons.js'; +import { + i18nConditionalGet, + specialPromptToContextMenuID, + contextMenuIconsPath +} from '../../js/mzta-utils.js'; +import { + customMenuIcons, + customMenuIconsPath +} from './mzta-custom-menu-icons.js'; // Convert "moz-extension:images/foo.png" to a relative path usable from this page function resolveSpecialIconPath(promptId) { From 4c7b38fffcaa0d390351fbda1f87760d6b742efb Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 17 Apr 2026 01:53:00 +0200 Subject: [PATCH 30/37] added other context menu icons. see #680 #184 --- README.md | 1 + images/context_menu/custom/apple.png | Bin 857 -> 0 bytes images/context_menu/custom/coffee.png | Bin 987 -> 0 bytes images/context_menu/custom/idea.png | Bin 0 -> 793 bytes images/context_menu/custom/profit.png | Bin 0 -> 664 bytes images/context_menu/custom/science.png | Bin 0 -> 1938 bytes images/context_menu/custom/send.png | Bin 859 -> 0 bytes images/context_menu/custom/start-up.png | Bin 0 -> 1298 bytes images/context_menu/custom/statistics.png | Bin 1083 -> 0 bytes images/context_menu/getcalendarevent.png | Bin 0 -> 1334 bytes .../getcalendareventfromclipboard.png | Bin 0 -> 1536 bytes images/context_menu/gettask.png | Bin 0 -> 1055 bytes js/mzta-utils.js | 9 +++++++++ pages/menu_order/mzta-custom-menu-icons.js | 8 ++++---- 14 files changed, 14 insertions(+), 4 deletions(-) delete mode 100644 images/context_menu/custom/apple.png delete mode 100644 images/context_menu/custom/coffee.png create mode 100644 images/context_menu/custom/idea.png create mode 100644 images/context_menu/custom/profit.png create mode 100644 images/context_menu/custom/science.png delete mode 100644 images/context_menu/custom/send.png create mode 100644 images/context_menu/custom/start-up.png delete mode 100644 images/context_menu/custom/statistics.png create mode 100644 images/context_menu/getcalendarevent.png create mode 100644 images/context_menu/getcalendareventfromclipboard.png create mode 100644 images/context_menu/gettask.png diff --git a/README.md b/README.md index dea7963d..ba125488 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,7 @@ _The language status represents the percentage of translated strings in the late - [HideMau](https://www.flaticon.com/authors/hidemaru) for the ai summarize icon - [Hilmy Abiyyu A.](https://www.flaticon.com/authors/hilmy-abiyyu-a) for the ai translate and context menu icons - [bearicons](https://www.flaticon.com/authors/bearicons) for the empty context menu icon +- [meaicon](https://www.flaticon.com/authors/meaicon) for the add task context menu icon
    diff --git a/images/context_menu/custom/apple.png b/images/context_menu/custom/apple.png deleted file mode 100644 index ec1682264ebd16390c6bf2afc9b0433a9e2eee43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 857 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 yE$Mibe5bGW_MU*e1fi9j(J0t z+k$(2FX!7IyEuExT4x-e|ITyyKA-cP-}(K{flt`%qZZ!N(6BIc?ka##dUS0(zVoB# zxZxH^vf74*`$MxQkme!HE3(!noB#OP7rJ+`Ue7RQIC_!c0@S8bx0~7Zf+bb)_;!G{ ztwqhTk?)salw>jp_~HMnH>?RHw{4pa+$vaex;elo-aqcOfaRd0^Ne$cqdB}skk9`F zc(Ky=f7#bU$y^!rlZ=2bTA*`;7 z$2ScnmJEWWz;ESw6&8k*kR|{HzzM+%r=rovt4m8W{}rGvojyCC%{~cC7wk58r8iWB zbcLCntDfuUksdbZHdp%o;bdduRFl3TxEkrw-kLXnLqI!F3|t5t3SoKQH>MvzG6;SL zT#R#9Cd$g*=o_mEg1?yAI|dD*bdA9>V4UEH!9?IaU^{RjW+Z%nVL$DyTZ?h-9^gw6 z=ca$Jtn6Lrvj*zx>!Su=k@VAn&3j{W1iLDI{|S?}(KDThNuvQXv)!KOZ7^vY(q;Aa z_0hf>iHeFtquScyfU?q8Py0UrShZ?ZP7t-8k@d5-F=J)|g}E?nt_^|!v%NsxIXAB| z9v>^S#gb-(`TPN37BITAsA&2?oz<&XXMq+6g@XXhOa}X8_QipPUKpMSgi*8Yg3rtB znnXN4e@$80(`KeB-`{a^(xlm#{YYjj0ENQVqa;Tegf3Pn_6g(k$SWM0xr5Zz?Tqdw0eK-^_Hhz8Q+W zq1YRSoiA%{$z=eEc>HzX1%v$tsbmmT18it)oQ&DcGP|>S<;t!h0jf$%J2CqX5U)+8 z)@CD-ogqw=wB6t>fVXDMIP+vCvvGvy_1$o$grTK1e|`agq&7)Sl2!t@)uvL(2n2$+ zPDLU$eR~et39EenPrx05y98s&<-TBMIeHhT(Y-t;dD$==SUQES*4aHdv|%>7s=R!Q z=iD+$Yx2^i^1LMjH=;oRlFZCXkUC5%c9F>E`e!%;INsS!P6Hr9>*uJiuaAadxCFDT zNd-Exoj!Z=!Gp)g#$q={%<{P~q@|;imiCUkVTlR54z>X}X`;bWnN4@F zpvw1mTPCv(DH1Cv_`%Qs-72OdGm^Z>SbhYBokakZ@%Vjm?iZL9<+ItBl0i^0>0tA( z&M6#s*0@oz%5ep;H512-A78xxpTDk2r$;7(;AeuFA-osOdgbKJaSY z8BI;6Y8o1rST?&4xDhisvs=EqeEG4ycPr=q08Ab~ZQ7L6zJ6asYmQxhK+gR+;rloA zD}+}8&w1XxVJ`O@XLgq}`&&&A>~Lm#YE!9FGn);hL#TJ~6g@TlEuZhkjBF$_YVZVr z(5wk*(S2LCjPL6|Icd@Xpp9H^?%MM5XIwrXN80RQk)-to7f8B0lF#4lN#_A=t;NMJ zkvuE?lhj)L3G4osxE2zuzvs3YJ%Y5WDqn-8U!xMYJ=dpWGeO0 za5RVYRUDe#29zX&VD-RQva#{of|;Rnw|;B@>wNze;KycGThq|6Px# literal 0 HcmV?d00001 diff --git a/images/context_menu/custom/send.png b/images/context_menu/custom/send.png deleted file mode 100644 index d6aefcd8be4663ba7daf873c427833de5d3fd677..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 859 zcmV-h1El2IW!)gdI3cs3G4tCMf60} z-x!`Rz`Jo+O;zCb`k@v8$&gozu+Y_!IuWu}p0L}Yi7(|O8jv$!ehQQx2!(4iX0tFk z0s2nG!9DtS?;fa@tbo%6n<;VY$sXuFQc06qhwm{_gv}0;0@Wo zhBXc5XJIK0ZnuD@QX;maL%1VJkW50lao^OP14mVjJH4+eQ8pWdA_81iCIgxVdSO>K z3EUp=`!Jcird5jDw*%#;k*f}j(+R2yxhxb4Aju-OIu5|+2gL$fvetp!7d1Vgs?Zr~ z-uob(hC~7}orD@Y;PyZu&>V1CE*B^=tgMJ3MfD&u$N=E=HXmM=LxpG*6a^q{EdJils*l**^wV(mHRh7002ovPDHLkV1ka$b07c! diff --git a/images/context_menu/custom/start-up.png b/images/context_menu/custom/start-up.png new file mode 100644 index 0000000000000000000000000000000000000000..4a86d4f4c0350d06ce9976dcc137635424ddb665 GIT binary patch literal 1298 zcmV+t1?~EYP)K;3jXcyL_0x#zh1jP;~8LPoyRwHgn;UC z-_S?^=Ugth2L`}Gz@CwTih@FEA3!N+<`#DlZ{*e(JIn|6YMTx;0WEnM8YhX4SWBmH7SAM^+Am#1RmIX6yt-&{M~tAU$dwgK)`|!Yf{+CO z!DpWX8w@d+-u`|7KxJtej43GqfaN8n=yAJoy{|8^An=b#3E-9G6++-J2qpkf3X2hb zV9lgrS#y|z82}(j2{SHK;2W)D(p#W1IRQj3ap;Ms07@L0oXkuBH`+cr7Q#pzS|vykcpQU~zv`OinJstE(myx~#AW96>7>9A8|Y0k1*tBEKazccMi==S+)=0=V) zGVsVm>s=r2;$bwX8rjeGW-1~8hHWv=pi zy_JG=+n8u2L;T6WA$z*@FkafcQjqkdCP%-dwvI1g`;Dy@SfR>zqE(o#yqy78ygBHt`#=U#L zO~^n}%D*4&lEka!)^jJ4B?sa<@WJlg+R85|s#+}**(6mx4qzSuza~_$Fz;hryZi%M z&s-vaPBxS-N|7>oY!#!2BO^di(mwW-^pl1Za<*CBC}C(0GiI ziE(O=AIB!kjNZPDzpRY*mR623jFjjjB)}5K(1{*gAf3r@^~SF(udLv7I5^*SjuW8} z0FlrWoT{#2ZfTKQQE(k7^pB44dn^V(L)}SQ zo0|AM{u5sfd;x%6lKD3B4$;a8k#HDI*Ks+We>KR3?|j~`z{KPvs`eq0{AYTP;FJ#l4R2z%;FRDkJsfE~SSY@+S~1Xwu2NKH2~P002ovPDHLkV1l|P B2qXXi diff --git a/images/context_menu/getcalendarevent.png b/images/context_menu/getcalendarevent.png new file mode 100644 index 0000000000000000000000000000000000000000..e29dd99347efa897adaa9a46c30a575197e68aa1 GIT binary patch literal 1334 zcmV-61P!rw^<9HZ!~Krsy9o-1(p9 zInV!|=Q+-+qP>Q=P<+HiHGaH7-^!>+K((?=X8hCs75#pLYXg?)c!H7 zfKl3K30S|st^9nKyDKG?NwzP8hvcG3j4y*ogqo|b$7>Udt>GpbYX5U$aWWbdjdzu3 z>)(HT+=+aC8o;_=u2g(d8*jBVT_}d$J2DZlQV@yocU(k&z3L4ADMRd!Ty>&Abz#6g z{>G2-RUbVO-IL2r0r>rN;#eZ>h=B=lRRa)clPgX9_Ia?|YX(%JIg;-w1N9fT|3x7fmQ5@}4K@uq-mRN2#fy zDGEVNA*3sayhmOh0h6Oa06bq}1{6w6Eidm&C98&tDxnJDY7TbXoK`5Df_) zs?gEiZF$7qRCQ=*tpBwQrIcLi_Q<$C^&Ku7k0wdFux56M2g@`g}|4PaeaEy9vx}PD|Ow}ce=kh zA^TC*PR~HTB6NJtT~2X2mgGX*LdtCZRBI2(w1@3WHXco~pgh3#rA1U0YG{feb|t~K z9mg;O3KI%-DhoAsEGweY)E4TNQFA?3P618rX=2?O+7dQR?P)qvZWe#CB~EKslJ76r z#M6=wmlmO`{nhqo{Ux^V>c12f1Qm)+g^wSLfMg9@8_V+w@O(Mww=4ZDKAE<$U5~nE zo8{9CDk3ANt|+KdRiH9HBn;hGR^$~B8c+uUFm*MHcRZgWGe|?1$NcgeJHmwkGay(t zIS7bZFk~CbYssEc8lpI;<4S)}YaPfJ2P-Kt4J^l^K+pb;@@N=<&-b2W^~yPYd_WbH z1_dCC`c^;@!~qf+A1Qm4mWDOTB7s3Ibs#^nsJADS1O*q9*`06R%nIIqbqVi$c7#*) zmj{hCba)8FrM?x=ROKri?}M}>Y3;HJs7n6(`Zc)My6@aFo!WnF*3J!Q*{ohUhxzwa zFk|YtKCUw@Inb2B!#~uw0@KRPZ4pD+hXB`ulj)bUi&a!10*P;TAim(AoD+ zz|i|o&D{-YJYO_Mo2vF?2^cfBv_ZhN`^--EN+M>)6^@uKUUg sq_`hsi4Y>@De|xvv}>>T|2qo*0Uml-g-M;w<^TWy07*qoM6N<$g2c{p_5c6? literal 0 HcmV?d00001 diff --git a/images/context_menu/getcalendareventfromclipboard.png b/images/context_menu/getcalendareventfromclipboard.png new file mode 100644 index 0000000000000000000000000000000000000000..f5f73283b87e527be12624d614b5f9c84760641e GIT binary patch literal 1536 zcmZ8h2{e>z82U+72ltvQBC& zSudA6L$s=-B4x5fvL#zexxeb1d(M5&dA{d)m+yVQ^L^)hX%2QY1z9y&000!|`zTHj zYpwwy1+9@B&OAh5q!Wz{2)Z_mLk|2f$(95FRcGW@n39k$6TZ(q65@$9fJW?>B1ou4 zQQe}P!vdqCnGuHo2WD_+l(8MvMbp%nU`*Jq0@9+OqJ~3m^gue@5I{hVB%lUL0wARJ zL%{vgYsc9PO8kh!AWZ;afHK4$5Q0y^f5h34Ub7G^0KAv@kN-av0HEYqX#ft@;Sqr3BBX-RG0O%)plTw9RHQgtfnkhQ7L_dB1vfD zBPazpyJnnBq4v2{q-!jQXN#6XRtr%`@QxvE0MFbp)p|3U+>i1y=s9O(_BxTab6%^2 zN?!2dr$l7z@yl#6=sQ_9DfYbO{$`U=x2q~KICh~i5mXLc4r2NGYo9)FEhXx}osJkZr#`Y)YN4ep$^;voh)|}(l!bLu63rf~ z0n;4|@yQF;rM)69^7V23Pq|Ds(S!o;YV^MdqAn~F&+gBAzaoC+z^_)2>`Xas7kME}qD5Ls+s$yEX|!$xjS0FvgPEwY;vD^bY|bMJ5U%ea`My1asz zc2nW{Inz9>c7y`*X_c@c9zl}0=LRQig=y!z-1a?ZT|X4mB|37vs)tfyPrcrGkBfv^ zedlNtM=WDhD9JGiR*RFpGWRSp$Es;YXJ(&jbpgJdrwtCnHN+cu`9(uSU-B8;Iz{b4 zHBIdiImsN%shR%i5)1e2bM#`5Q9*#XJUyon7lH!rDv#>`LE$35K1%!}(I@qkcdQ_3 z>bU{~Ll^37Mv-7xc%UQqk|3boSP5D! zu|Yme<-2+|@11RX&?Z3KxfhVim-QQaHXFZ_l>kfc%gglEdW>27!{}5yih%6P{THwq BcE$hz literal 0 HcmV?d00001 diff --git a/images/context_menu/gettask.png b/images/context_menu/gettask.png new file mode 100644 index 0000000000000000000000000000000000000000..0905dd77704ab042eec555072551dd679954c4bc GIT binary patch literal 1055 zcmV+)1mOFLP)&F<7X)yT?gj2l;6C*t%%QtpP>?&k2%beK(BZGObO^3zF27sQG6RYPFd$st zDg(exWHNQNYL2;cB-8nKsI5=YXF=Aq&;Tgs5e~W~ckr*?J>#_oj=a0vEKa{8@C5(f z?f(l3j9B74Mj|t70GP#@K7nUiAb!>`ctBymXN~|5n=I>xN)4#{dy~Q-jN|J1LOlJs zolIwe%RaN!Ku=c<*jopV0mDs+m-Jrip@B(&=xFXapv!Z*@bPRS{aw`nfDvkY0k{-E zenN}JhKjhE&uvrD_k7G=XcOa=s%~XacyQO0Dmy)BB}7sxvIoJLmuwtY@3wnW69CcC z+zvqp@qJ(9#Vhet^>}KZVi9MqNEhL_m3VC*KqSBSXG9JG=N4@H4i)`?|NO$bH`hp3 z1|4IWSW(-~0N2%FSt8t3Ik#v&7uRs;l(ps6^NooEP*B6@w&SU@H3lO2>_OlWAL5^a zv1vA$Ix#gi zm&jY8$`{eG>@G!bQ`Mb8!~tB~PAC_l73vs{macA6l}GXE5z6RNWiX&FKvCgoqmL;@MtaqL-|$UkQ$+ntinE_E|8a9C89nQ|h6g~wC^W!R< zYS@4kPY;R8(}Lziq}Ou8E3IUDy=6Na6@FaI&?yav@3)ibEtc)9P^CwaH$Y#qF2p>) zj)5+-Ir8Q$5^_%fv2qqiUENUbD;h$epHsUxHKDlu%oSHrVS5nqrqMOD@7ng!> 'compose_action_menu'; export const getMenuContextDisplay = () => 'message_display_action_menu'; +export const contextMenuID_GetCalendarEvent = 'mzta-get-calendar-event'; +export const contextMenuID_GetCalendarEventFromClipboard = 'mzta-get-calendar-event-from-clipboard'; +export const contextMenuID_GetTask = 'mzta-get-task'; export const contextMenuID_AddTags = 'mzta-add-tags'; export const contextMenuID_Spamfilter = 'mzta-spamfilter'; export const contextMenuID_Summarize = 'mzta-summarize'; export const contextMenuID_Translate = 'mzta-translate'; export const contextMenuIconsPath = { + [contextMenuID_GetCalendarEvent]: 'moz-extension:images/context_menu/getcalendarevent.png', + [contextMenuID_GetCalendarEventFromClipboard]: 'moz-extension:images/context_menu/getcalendareventfromclipboard.png', + [contextMenuID_GetTask]: 'moz-extension:images/context_menu/gettask.png', [contextMenuID_AddTags]: 'moz-extension:images/context_menu/autotags.png', [contextMenuID_Spamfilter]: 'moz-extension:images/context_menu/spamfilter.png', [contextMenuID_Summarize]: 'moz-extension:images/context_menu/summarize.png', @@ -43,6 +49,9 @@ export const contextMenuIconsPath = { // Map from special prompt IDs to context menu IDs export const specialPromptToContextMenuID = { + 'prompt_get_calendar_event': contextMenuID_GetCalendarEvent, + 'prompt_get_calendar_event_from_clipboard': contextMenuID_GetCalendarEventFromClipboard, + 'prompt_get_task': contextMenuID_GetTask, 'prompt_add_tags': contextMenuID_AddTags, 'prompt_spamfilter': contextMenuID_Spamfilter, 'prompt_summarize': contextMenuID_Summarize, diff --git a/pages/menu_order/mzta-custom-menu-icons.js b/pages/menu_order/mzta-custom-menu-icons.js index a161203d..58020141 100644 --- a/pages/menu_order/mzta-custom-menu-icons.js +++ b/pages/menu_order/mzta-custom-menu-icons.js @@ -22,12 +22,10 @@ export const customMenuIcons = [ 'adventure-game.png', 'airport.png', 'alert-sign.png', - 'apple.png', 'calendar.png', 'cash.png', 'clapboard.png', 'clock.png', - 'coffee.png', 'coins.png', 'copywriting.png', 'customer-service.png', @@ -39,6 +37,7 @@ export const customMenuIcons = [ 'express-delivery.png', 'flammable.png', 'home.png', + 'idea.png', 'info.png', 'invoice.png', 'justice-scale.png', @@ -46,14 +45,15 @@ export const customMenuIcons = [ 'love-letter.png', 'policeman.png', 'printer.png', + 'profit.png', 'puzzle-game.png', 'radiation-hazard.png', 'receipt.png', 'recycle-sign.png', + 'science.png', 'scissors.png', - 'send.png', 'star.png', - 'statistics.png', + 'start-up.png', 'sticky-notes.png', 'target.png', 'thermometer.png', From 35bc236ce5b32a875522ff14546fe4067fac2595 Mon Sep 17 00:00:00 2001 From: Mic Date: Fri, 17 Apr 2026 01:59:00 +0200 Subject: [PATCH 31/37] added a border to editable icons. see #680 #184 --- pages/menu_order/mzta-menu-order.css | 8 ++++++++ pages/menu_order/mzta-menu-order.js | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pages/menu_order/mzta-menu-order.css b/pages/menu_order/mzta-menu-order.css index 2fb990f5..fbb64a68 100644 --- a/pages/menu_order/mzta-menu-order.css +++ b/pages/menu_order/mzta-menu-order.css @@ -193,6 +193,10 @@ background: transparent; } +.item_icon_preview_editable { + border-color: #b0c4de; +} + .item_icon_preview_special { cursor: default; } @@ -348,6 +352,10 @@ color: #ffb74d; } + .item_icon_preview_editable { + border-color: #556; + } + .item_icon_preview:hover { background: #3a3a44; border-color: #409df3; diff --git a/pages/menu_order/mzta-menu-order.js b/pages/menu_order/mzta-menu-order.js index 9e02d7dc..fa8b89b3 100644 --- a/pages/menu_order/mzta-menu-order.js +++ b/pages/menu_order/mzta-menu-order.js @@ -304,7 +304,7 @@ function buildSpecialIconDisplay(prompt) { function buildIconPicker(prompt) { const preview = document.createElement('img'); - preview.classList.add('item_icon_preview'); + preview.classList.add('item_icon_preview', 'item_icon_preview_editable'); preview.alt = ''; preview.title = browser.i18n.getMessage('menu_order_icon_label'); applyIconToPreview(preview, prompt.custom_icon || ''); From 8ba30a1b7ce5c3f867552493a227e4b5beb3a469 Mon Sep 17 00:00:00 2001 From: mic Date: Fri, 17 Apr 2026 22:16:14 +0200 Subject: [PATCH 32/37] release notes updated --- CHANGELOG.md | 2 +- options/mzta-release-notes.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0baa27ad..e60d4289 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@
  • Antispam information are now permanently saved for each message [#675].
  • [All APIs] A summary has been added above the mail content [#580].
  • [All APIs] Inline auto translation for emails added [#247].
  • -
  • Custom menus configuration added. Now it's possibile to define which prompts show in the ThunderAI menu, which ones in the context menu and in which order [#680].
  • +
  • Custom menus configuration added. Now it's possibile to define which prompts show in the ThunderAI menu, which ones in the context menu and in which order [#49, #184, #680].
  • ...
  • Version 4.0.3 - 20/03/2026

    diff --git a/options/mzta-release-notes.html b/options/mzta-release-notes.html index a204a653..a60aeaba 100644 --- a/options/mzta-release-notes.html +++ b/options/mzta-release-notes.html @@ -12,7 +12,7 @@
  • Antispam information are now permanently saved for each message [#675].
  • [All APIs] A summary has been added above the mail content [#580].
  • [All APIs] Inline auto translation for emails added [#247].
  • -
  • Custom menus configuration added. Now it's possibile to define which prompts show in the ThunderAI menu, which ones in the context menu and in which order [#680].
  • +
  • Custom menus configuration added. Now it's possibile to define which prompts show in the ThunderAI menu, which ones in the context menu and in which order [#49, #184, #680].
  • ...
  • Version 4.0.3 - 20/03/2026

    From d383e78503205b0918904391f0116fbbf3aec1e5 Mon Sep 17 00:00:00 2001 From: mic Date: Fri, 17 Apr 2026 22:26:29 +0200 Subject: [PATCH 33/37] old prompt_summarize_this prompt removed --- _locales/en/messages.json | 10 +--------- js/mzta-prompts.js | 19 ------------------- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 7d45ba67..7384eb00 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -35,10 +35,6 @@ "message": "Classify", "description": "" }, - "prompt_summarize_this": { - "message": "Summarize", - "description": "" - }, "prompt_translate_this": { "message": "Translate", "description": "" @@ -824,10 +820,6 @@ "message": "Classify the following text in terms of Politeness, Warmth, Formality, Assertiveness, Offensiveness giving a percentage for each category. Reply with only the category and score with no extra comments or other text.", "description": "" }, - "prompt_summarize_this_full_text": { - "message": "Summarize the following email into a bullet point list.", - "description": "" - }, "prompt_translate_this_full_text": { "message": "Translate the email below into {%thunderai_translate_lang%}.\n\nRules:\n- Translate both the subject and the body.\n- Return the result as a JSON object with three fields: \"subject\", \"body\" and \"status\".\n- If the translation has been done the status is equal to 1.\n- If the email is written in one of these languages \"{%thunderai_translate_exclude_lang%}\" or in the {%thunderai_translate_lang%} language, return an empty string for the body and the subject and set the status to -1.\n- Do not add explanations, notes, or any text outside the JSON.\n\nMail subject: {%mail_subject%}\n\nMail body: {%mail_html_body%}\n\nGenerate a response in JSON format only. The output should be only a JSON object. Here is an example of the JSON format to be used:\n{\n\"subject\": \"subject translation\",\n\"body\": \"body translation\",\n\"status\": \"status result\"\n}", "description": "" @@ -1113,7 +1105,7 @@ "description": "" }, "prompt_summarize": { - "message": "Summarize this email or these emails", + "message": "Summarize", "description": "" }, "prompt_summarize_full_text": { diff --git a/js/mzta-prompts.js b/js/mzta-prompts.js index 02c52545..39b46c46 100644 --- a/js/mzta-prompts.js +++ b/js/mzta-prompts.js @@ -211,25 +211,6 @@ const defaultPrompts = [ is_special: "0", show_in: "popup", }, - { - id: 'prompt_summarize_this', - name: "__MSG_prompt_summarize_this__", - text: "prompt_summarize_this_full_text", - type: "0", - action: "0", - need_selected: "0", - need_signature: "0", - need_custom_text: "0", - define_response_lang: "1", - use_diff_viewer: "0", - chatgpt_web_model: '', - chatgpt_web_project: '', - chatgpt_web_custom_gpt: '', - api_type: '', - is_default: "1", - is_special: "0", - show_in: "popup", - }, { id: 'prompt_proofread_this', name: "__MSG_prompt_proofread_this__", From 0a99b28067f428b92f93024f3eb705c23325faa9 Mon Sep 17 00:00:00 2001 From: mic Date: Fri, 17 Apr 2026 22:29:08 +0200 Subject: [PATCH 34/37] typo fix --- js/mzta-menus.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/mzta-menus.js b/js/mzta-menus.js index 6fd63209..f6196075 100644 --- a/js/mzta-menus.js +++ b/js/mzta-menus.js @@ -218,7 +218,7 @@ export class mzta_Menus { // const tabs = await browser.tabs.query({ active: true, currentWindow: true }); // add custom text if needed - //browser.runtime.sendMessage({command: "chatgpt_open", prompt: fullPrompt, action: curr_prompt.action, tabId: tabs[0].id}); + // browser.runtime.sendMessage({command: "chatgpt_open", prompt: fullPrompt, action: curr_prompt.action, tabId: tabs[0].id}); if(curr_prompt.is_special == '1'){ // Special prompts switch(curr_prompt.id){ case 'prompt_add_tags': { // Add tags to the email From 9fac73c3194d7f341f283e214a2a040f65cde50d Mon Sep 17 00:00:00 2001 From: mic Date: Fri, 17 Apr 2026 22:57:19 +0200 Subject: [PATCH 35/37] working on special prompt on popup menu. see #680 --- mzta-background.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/mzta-background.js b/mzta-background.js index 646a0e1c..38914883 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -530,6 +530,21 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => { break; case 'shortcut_do_prompt': taLog.log("Executing shortcut, promptId: " + message.promptId); + if (specialContextMenuActions[message.promptId]) { + async function _shortcut_special() { + let tabId = message.tabId; + if (!tabId) { + let tabs = await browser.tabs.query({ active: true, currentWindow: true }); + if (tabs.length === 0) return false; + tabId = tabs[0].id; + } + let displayedMessage = await browser.messageDisplay.getDisplayedMessage(tabId); + if (!displayedMessage) return false; + taLog.log("Displayed message found."); + return specialContextMenuActions[message.promptId]([displayedMessage]); + } + return _shortcut_special(); + } return menus.executeMenuAction(message.promptId); break; case 'popup_menu_ready': From 1baac228c05b085afb04b8173b86174857732e18 Mon Sep 17 00:00:00 2001 From: mic Date: Sat, 18 Apr 2026 23:06:34 +0200 Subject: [PATCH 36/37] addtags popup menu fixed --- mzta-background.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mzta-background.js b/mzta-background.js index 38914883..5352881e 100644 --- a/mzta-background.js +++ b/mzta-background.js @@ -530,7 +530,7 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => { break; case 'shortcut_do_prompt': taLog.log("Executing shortcut, promptId: " + message.promptId); - if (specialContextMenuActions[message.promptId]) { + if (message.promptId !== 'prompt_add_tags' && specialContextMenuActions[message.promptId]) { //TODO Add an option here if you want the user to decide to use the autotagging also in the popup menu async function _shortcut_special() { let tabId = message.tabId; if (!tabId) { From 8b1e130bb486e74208bbf8b026e20d63560eb2ee Mon Sep 17 00:00:00 2001 From: mic Date: Sun, 19 Apr 2026 22:17:48 +0200 Subject: [PATCH 37/37] default menu items show_in fixed to have the same old behaviour. see #680 --- js/mzta-prompts.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/js/mzta-prompts.js b/js/mzta-prompts.js index 39b46c46..d081829b 100644 --- a/js/mzta-prompts.js +++ b/js/mzta-prompts.js @@ -282,7 +282,7 @@ const specialPrompts = [ api_type: '', is_default: "1", is_special: "1", - show_in: "both", + show_in: "popup", }, { id: 'prompt_get_calendar_event_from_clipboard', @@ -298,7 +298,7 @@ const specialPrompts = [ api_type: '', is_default: "1", is_special: "1", - show_in: "both", + show_in: "popup", }, { id: 'prompt_get_task', @@ -314,7 +314,7 @@ const specialPrompts = [ api_type: '', is_default: "1", is_special: "1", - show_in: "both", + show_in: "popup", }, { id: 'prompt_spamfilter', @@ -330,7 +330,7 @@ const specialPrompts = [ api_type: '', is_default: "1", is_special: "1", - show_in: "both", + show_in: "context", }, { id: 'prompt_summarize', @@ -347,7 +347,7 @@ const specialPrompts = [ api_model: '', is_default: "1", is_special: "1", - show_in: "both", + show_in: "context", }, { id: 'prompt_summarize_email_template', @@ -398,7 +398,7 @@ const specialPrompts = [ api_model: '', is_default: "1", is_special: "1", - show_in: "both", + show_in: "context", } ];