new menus first try. see #680
This commit is contained in:
parent
f64d49ccf1
commit
774c63ef9e
9 changed files with 239 additions and 287 deletions
|
|
@ -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": ""
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue