new menus first try. see #680

This commit is contained in:
Mic 2026-04-02 00:23:00 +02:00
parent f64d49ccf1
commit 774c63ef9e
9 changed files with 239 additions and 287 deletions

View file

@ -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": ""

View file

@ -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:

View file

@ -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");
}

View file

@ -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 });

View file

@ -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);

View file

@ -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-<prompt_id>'.
// 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);
}
});

View file

@ -51,6 +51,15 @@
<option value="2">__MSG_customPrompts_add_to_menu_composing__</option>
</select>
<br><br>
<label for="selectShowInNew" class="field_title">__MSG_show_in__:</label>
<br>
<select id="selectShowInNew" name="show_in" tabindex="4">
<option value="popup">__MSG_show_in_popup__</option>
<option value="context">__MSG_show_in_context__</option>
<option value="both">__MSG_show_in_both__</option>
<option value="none">__MSG_show_in_none__</option>
</select>
<br><br>
<label for="selectActionNew" class="field_title">__MSG_customPrompts_form_label_Action__:</label>
<br>
<select id="selectActionNew" name="action" tabindex="5">

View file

@ -290,7 +290,8 @@ document.addEventListener('DOMContentLoaded', async () => {
position_display: positionMax_display + 1,
is_default: 0,
idnum: idnumMax + 1,
api_type: document.getElementById('new_prompt_api_type').value
api_type: document.getElementById('new_prompt_api_type').value,
show_in: document.getElementById('selectShowInNew').value,
};
switch(prefs.connection_type) {
@ -687,6 +688,8 @@ function showItemRowEditor(tr) {
tr.querySelector('.api_additional_info_show').style.display = 'none';
tr.querySelector('.type_output').style.display = 'inline';
tr.querySelector('.type_show').style.display = 'none';
tr.querySelector('.show_in_output').style.display = 'inline';
tr.querySelector('.show_in_show').style.display = 'none';
const action_output = tr.querySelector('.action_output')
action_output.style.display = 'inline';
action_output.addEventListener('change', toggleDiffviewer);
@ -712,6 +715,8 @@ function hideItemRowEditor(tr) {
toggleAdditionalPropertiesShow(tr);
tr.querySelector('.type_output').style.display = 'none';
tr.querySelector('.type_show').style.display = 'inline';
tr.querySelector('.show_in_output').style.display = 'none';
tr.querySelector('.show_in_show').style.display = 'inline';
const action_output = tr.querySelector('.action_output')
action_output.style.display = 'none';
action_output.addEventListener('change', toggleDiffviewer);
@ -848,6 +853,7 @@ function handleCancelClick(e) {
tr.querySelector('.text_output').value = sanitizeHtml(tr.querySelector('.text_show').innerHTML).replace(/<br\s*\/?>/gi, "\n");
tr.querySelector('.type_output').value = tr.querySelector('.type').innerText;
// tr.querySelector('.type_output').selectedOptions[0].text = tr.querySelector('.type_show').innerText;
tr.querySelector('.show_in_output').value = tr.querySelector('.show_in').innerText || 'popup';
tr.querySelector('.action_output').value = tr.querySelector('.action').innerText;
// tr.querySelector('.action_output').selectedOptions[0].text = tr.querySelector('.action_show').innerText;
tr.querySelector('.chatgpt_web_model_output').value = tr.querySelector('.chatgpt_web_model_show').innerText;
@ -874,6 +880,7 @@ function handleConfirmClick(e) {
newValues.name = tr.querySelector('.name_output').value.trim();
newValues.text = tr.querySelector('.text_output').value;
newValues.type = tr.querySelector('.type_output').value;
newValues.show_in = tr.querySelector('.show_in_output').value;
newValues.action = tr.querySelector('.action_output').value;
newValues.need_selected = tr.querySelector('.need_selected').checked ? 1 : 0;
newValues.need_signature = tr.querySelector('.need_signature').checked ? 1 : 0;
@ -900,6 +907,8 @@ function handleConfirmClick(e) {
// Update item data
tr.querySelector('.type').innerText = tr.querySelector('.type_output').value;
tr.querySelector('.type_show').innerText = tr.querySelector('.type_output').selectedOptions[0].text;
tr.querySelector('.show_in').innerText = tr.querySelector('.show_in_output').value;
tr.querySelector('.show_in_show').innerText = tr.querySelector('.show_in_output').selectedOptions[0].text;
tr.querySelector('.action').innerText = tr.querySelector('.action_output').value;
tr.querySelector('.action_show').innerText = tr.querySelector('.action_output').selectedOptions[0].text;
if (newValues.api_type !== '') {
@ -975,6 +984,7 @@ function handleCopyClick(e) {
document.getElementById('txtNameNew').value = name + ' (' + browser.i18n.getMessage("copy_text") + ')';
document.getElementById('txtTextNew').value = text;
document.getElementById('selectTypeNew').value = type;
document.getElementById('selectShowInNew').value = tr.querySelector('.show_in_output').value || 'popup';
document.getElementById('selectActionNew').value = action;
document.getElementById('checkboxNeedSelectedNew').checked = need_selected;
@ -1043,7 +1053,7 @@ function loadPromptsList(values){
}
let options = {
valueNames: [ { data: ['idnum'] }, 'is_default', 'id', 'name', 'text', 'type', 'action', 'position_compose', 'position_display', { name: 'need_selected', attr: 'checked_val'}, { name: 'need_signature', attr: 'checked_val'}, { name: 'need_custom_text', attr: 'checked_val'}, { name: 'define_response_lang', attr: 'checked_val'}, { name: 'use_diff_viewer', attr: 'checked_val'}, { name: 'enabled', attr: 'checked_val'}, 'api_type', ...api_fields ],
valueNames: [ { data: ['idnum'] }, 'is_default', 'id', 'name', 'text', 'type', 'action', 'position_compose', 'position_display', 'show_in', { name: 'need_selected', attr: 'checked_val'}, { name: 'need_signature', attr: 'checked_val'}, { name: 'need_custom_text', attr: 'checked_val'}, { name: 'define_response_lang', attr: 'checked_val'}, { name: 'use_diff_viewer', attr: 'checked_val'}, { name: 'enabled', attr: 'checked_val'}, 'api_type', ...api_fields ],
item: function(values) {
let type_output = '';
switch(String(values.type)){
@ -1070,7 +1080,21 @@ function loadPromptsList(values){
action_output = `__MSG_customPrompts_substitute_text__`;
break;
}
//console.log('>>>>>>>>>>>>> action_output: ' + JSON.stringify(action_output));
let show_in_output = '';
switch(String(values.show_in || 'popup')){
case "popup":
show_in_output = `__MSG_show_in_popup__`;
break;
case "context":
show_in_output = `__MSG_show_in_context__`;
break;
case "both":
show_in_output = `__MSG_show_in_both__`;
break;
case "none":
show_in_output = `__MSG_show_in_none__`;
break;
}
let output = `<tr ` + ((values.is_default == 1) ? 'class="is_default"':'') + `>
<td class="w08"><span class="id id_show"></span><input type="text" class="hiddendata id_output" value="` + values.id + `" /></td>
@ -1120,6 +1144,17 @@ function loadPromptsList(values){
</select>` +
`<span class="type hiddendata"></span>
<br><br>
<span class="field_title_s">__MSG_show_in__:</span>
<br>
<span class="show_in_show">` + show_in_output + `</span>
<select class="show_in_output hiddendata input_mod">
<option value="popup"` + ((values.show_in == "popup" || !values.show_in) ? ' selected':'') + `>__MSG_show_in_popup__</option>
<option value="context"` + ((values.show_in == "context") ? ' selected':'') + `>__MSG_show_in_context__</option>
<option value="both"` + ((values.show_in == "both") ? ' selected':'') + `>__MSG_show_in_both__</option>
<option value="none"` + ((values.show_in == "none") ? ' selected':'') + `>__MSG_show_in_none__</option>
</select>` +
`<span class="show_in hiddendata"></span>
<br><br>
<span class="field_title_s">__MSG_customPrompts_form_label_Action__:</span>
<br><span class="action_show">` + action_output + `</span>
<select class="action_output hiddendata">
@ -1276,6 +1311,7 @@ function clearFields() {
document.getElementById('chatGPTWebProjectNew').value = '';
document.getElementById('chatGPTWebCustomGPTNew').value = '';
document.getElementById('selectTypeNew').value = '0';
document.getElementById('selectShowInNew').value = 'popup';
document.getElementById('selectActionNew').value = '0';
document.getElementById('checkboxNeedSelectedNew').value = '0';
document.getElementById('checkboxNeedSignatureNew').value = '0';

View file

@ -18,34 +18,15 @@
import { prefs_default } from "../options/mzta-options-default.js";
import { taLogger } from "../js/mzta-logger.js";
import {
checkSparksPresence,
checkAPIIntegration,
} from "../js/mzta-utils.js";
let menuSendImmediately = false;
let taLog = console;
let connection_type = 'chatgpt_web';
let add_tags = false;
let add_tags_use_specific_integration = false;
let add_tags_connection_type = '';
let get_calendar_event = false;
let get_calendar_event_from_clipboard = false;
let get_task = false;
let _ok_sparks = false;
let tabType;
let num_special_menu_items = 0;
document.addEventListener('DOMContentLoaded', async () => {
let prefs = await browser.storage.sync.get({
do_debug: prefs_default.do_debug,
dynamic_menu_force_enter: prefs_default.dynamic_menu_force_enter,
add_tags: prefs_default.add_tags,
add_tags_use_specific_integration: prefs_default.add_tags_use_specific_integration,
add_tags_connection_type: prefs_default.add_tags_connection_type,
get_calendar_event: prefs_default.get_calendar_event,
get_calendar_event_from_clipboard: prefs_default.get_calendar_event_from_clipboard,
get_task: prefs_default.get_task,
connection_type: prefs_default.connection_type
});
taLog = new taLogger("mzta-popup",prefs.do_debug);
@ -63,19 +44,7 @@ document.addEventListener('DOMContentLoaded', async () => {
});
taLog.log("active_prompts: " + JSON.stringify(active_prompts));
menuSendImmediately = prefs.dynamic_menu_force_enter;
connection_type = prefs.connection_type;
add_tags = prefs.add_tags;
add_tags_use_specific_integration = prefs.add_tags_use_specific_integration;
add_tags_connection_type = prefs.add_tags_connection_type;
get_calendar_event = prefs.get_calendar_event;
get_calendar_event_from_clipboard = prefs.get_calendar_event_from_clipboard;
get_task = prefs.get_task;
_ok_sparks = await checkSparksPresence() == 1;
// console.log(">>>>>>>>>>>>>>>>> add_tags: " + add_tags);
// console.log(">>>>>>>>>>>>>>>>> get_calendar_event: " + get_calendar_event);
// console.log(">>>>>>>>>>>>>>>>> get_task: " + get_task);
// console.log(">>>>>>>>>>>>>>>>> _ok_sparks: " + _ok_sparks);
searchPrompt(active_prompts, tabId, tabType);
searchPrompt(active_prompts, tabId, tabType, filtering);
i18n.updateDocument();
if(prefs.connection_type === 'chatgpt_web'){
@ -110,13 +79,17 @@ document.addEventListener('DOMContentLoaded', async () => {
}
}, { once: true });
async function searchPrompt(allPrompts, tabId, tabType){
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));
}
// console.log(">>>>>>>>> allPrompts: " + JSON.stringify(allPrompts));
@ -161,68 +134,10 @@ async function searchPrompt(allPrompts, tabId, tabType){
// Prepend numbers to the first 10 items
// If add_tags is true and connection_type is not 'chatgpt_web' reserve 0 position for prompt_add_tags and 1 for prompt_get_calendar_event (0, if no prompt_add_tags is disabled)
let max_num_el = 10
let first_num_el = 0;
let do_add_tags = checkDoAddTags();
let do_get_calendar_event = checkDoCalendarEvent();
let do_get_calendar_event_from_clipboard = checkDoCalendarEventFromClipboard();
let do_get_task = checkDoTask();
// console.log(">>>>>>>>>>> do_add_tags: " + do_add_tags);
// console.log(">>>>>>>>>>> do_get_calendar_event: " + do_get_calendar_event);
// console.log(">>>>>>>>>>> do_get_task: " + do_get_task);
// console.log(">>>>>>>>>>> filteredData: " + JSON.stringify(filteredData));
num_special_menu_items = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0) + (do_get_calendar_event_from_clipboard ? 1 : 0) + (do_get_task ? 1 : 0);
// console.log(">>>>>>>>>>>> num_special_menu_items: " + num_special_menu_items);
if(num_special_menu_items > 0){
max_num_el -= num_special_menu_items;
first_num_el = num_special_menu_items;
// console.log(">>>>>>>>>>>>> max_num_el: " + max_num_el);
// console.log(">>>>>>>>>>>>> first_num_el: " + first_num_el);
if(do_add_tags){
filteredData = ensurePromptAddTagsFirst(filteredData);
if (!filteredData[0].numberPrepended) {
filteredData[0].numberPrepended = 'true';
filteredData[0].label = '0. ' + filteredData[0].label;
}
}
if(do_get_calendar_event){
filteredData = ensurePromptGetCalendarEventFirst(filteredData, do_add_tags);
let gce_curr_pos = do_add_tags ? 1 : 0;
if (!filteredData[gce_curr_pos].numberPrepended) {
filteredData[gce_curr_pos].numberPrepended = 'true';
filteredData[gce_curr_pos].label = gce_curr_pos + '. ' + filteredData[gce_curr_pos].label;
}
}
if(do_get_calendar_event_from_clipboard){
filteredData = ensurePromptGetCalendarEventFromClipboardFirst(filteredData, do_add_tags, do_get_calendar_event);
let gcefc_curr_pos = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0);
if (!filteredData[gcefc_curr_pos].numberPrepended) {
filteredData[gcefc_curr_pos].numberPrepended = 'true';
filteredData[gcefc_curr_pos].label = gcefc_curr_pos + '. ' + filteredData[gcefc_curr_pos].label;
}
}
if(do_get_task){
filteredData = ensurePromptGetTaskFirst(filteredData, do_add_tags, do_get_calendar_event, do_get_calendar_event_from_clipboard);
let gtask_curr_pos = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0) + (do_get_calendar_event_from_clipboard ? 1 : 0);
if (!filteredData[gtask_curr_pos].numberPrepended) {
filteredData[gtask_curr_pos].numberPrepended = 'true';
filteredData[gtask_curr_pos].label = gtask_curr_pos + '. ' + filteredData[gtask_curr_pos].label;
}
}
}
// console.log(">>>>>>>>>>> filteredData after special items check: " + JSON.stringify(filteredData));
Array.from(filteredData).slice(first_num_el, max_num_el).forEach((item, index) => {
let number = (index + first_num_el).toString();
// Check if the number is already prepended to avoid duplication
Array.from(filteredData).slice(0, 10).forEach((item, index) => {
if (!item.numberPrepended) {
item.label = `${number}. ${item.label}`;
item.numberPrepended = 'true'; // Mark as prepended
item.label = `${index}. ${item.label}`;
item.numberPrepended = 'true';
}
});
@ -234,7 +149,7 @@ async function searchPrompt(allPrompts, tabId, tabType){
itemDiv.classList.add('mzta_autocomplete-item');
itemDiv.textContent = item.label;
itemDiv.setAttribute('data-id', item.id);
if((item.id === 'prompt_add_tags')||(item.id === 'prompt_get_calendar_event')||(item.id === 'prompt_get_calendar_event_from_clipboard')||(item.id === 'prompt_get_task')){
if(item.is_special == "1"){
itemDiv.className += ' special_prompt';
}
@ -367,9 +282,15 @@ async function sendPrompt(prompt_id, tabId){
}
function filterPromptsForTab(prompts_data, filtering){
// If filtering is 0, return the original array without any filters (btw it should not happen)
// Filter by show_in: only show prompts visible in the popup
let filtered = prompts_data.filter(prompt => {
const showIn = prompt.show_in || "popup";
return showIn === "popup" || showIn === "both";
});
// If filtering is 0, return without type filter (should not happen)
if (filtering === 0) {
return prompts_data;
return filtered;
}
// Define the types to include based on the value of filtering
@ -379,95 +300,10 @@ function filterPromptsForTab(prompts_data, filtering){
} else if (filtering === 2) {
allowedTypes = ["0", "2"];
} else {
// If filtering has an unexpected value, return the original data
return prompts_data;
return filtered;
}
// Filter the array based on the allowed types
return prompts_data.filter(prompt => allowedTypes.includes(prompt.type));
return filtered.filter(prompt => allowedTypes.includes(prompt.type));
}
function checkDoAddTags(){
return add_tags && checkAPIIntegration(connection_type, add_tags_use_specific_integration,add_tags_connection_type) && (tabType !== 'messageCompose');
}
function checkDoCalendarEvent(){
return get_calendar_event && (connection_type !== "chatgpt_web" && tabType !== 'messageCompose') && _ok_sparks;
}
function checkDoCalendarEventFromClipboard(){
return get_calendar_event_from_clipboard && (connection_type !== "chatgpt_web" && tabType !== 'messageCompose') && _ok_sparks;
}
function checkDoTask(){
return get_task && (connection_type !== "chatgpt_web" && tabType !== 'messageCompose') && _ok_sparks;
}
function ensurePromptAddTagsFirst(arr) {
// Find the index of the object with id "prompt_add_tags"
const index = arr.findIndex(item => item.id === "prompt_add_tags");
// If found and not already the first element
if (index !== -1 && index !== 0) {
// Remove it from its current position
const [promptAddTags] = arr.splice(index, 1);
// Add it to the beginning of the array
arr.unshift(promptAddTags);
}
return arr;
}
function ensurePromptGetCalendarEventFirst(arr, do_add_tags) {
// Find the index of the object with id "prompt_get_calendar_event"
const index = arr.findIndex(item => item.id === "prompt_get_calendar_event");
// If found and needs repositioning
if (index !== -1 && (do_add_tags ? index !== 1 : index !== 0)) {
// Remove it from its current position
const [promptAddTags] = arr.splice(index, 1);
// Add it to the specified position
const targetPosition = do_add_tags ? 1 : 0;
arr.splice(targetPosition, 0, promptAddTags);
}
return arr;
}
function ensurePromptGetCalendarEventFromClipboardFirst(arr, do_add_tags, do_get_calendar_event) {
// Find the index of the object with id "prompt_get_calendar_event_from_clipboard"
const index = arr.findIndex(item => item.id === "prompt_get_calendar_event_from_clipboard");
const targetPosition = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0);
// If found and needs repositioning
if (index !== -1 && index !== targetPosition) {
// Remove it from its current position
const [promptAddTags] = arr.splice(index, 1);
// Add it to the specified position
arr.splice(targetPosition, 0, promptAddTags);
}
return arr;
}
function ensurePromptGetTaskFirst(arr, do_add_tags, do_get_calendar_event, do_get_calendar_event_from_clipboard) {
// Find the index of the object with id "prompt_get_task"
const index = arr.findIndex(item => item.id === "prompt_get_task");
// Determine the target position to insert "prompt_get_task" after calendar
const targetPosition = (do_add_tags ? 1 : 0) + (do_get_calendar_event ? 1 : 0) + (do_get_calendar_event_from_clipboard ? 1 : 0);
// If found and needs repositioning
if (index !== -1 && index !== targetPosition) {
// Remove it from its current position
const [promptGetTask] = arr.splice(index, 1);
// Add it to the specified position
arr.splice(targetPosition, 0, promptGetTask);
}
return arr;
}