parent
e6bc740adf
commit
cfc0aedb98
9 changed files with 334 additions and 12 deletions
|
|
@ -2314,5 +2314,13 @@
|
||||||
"menu_order_hidden_items": {
|
"menu_order_hidden_items": {
|
||||||
"message": "Hidden items",
|
"message": "Hidden items",
|
||||||
"description": "Section header for hidden menu 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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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_compose` | number | Sort order for the popup menu in compose view |
|
||||||
| `position_context` | number | Sort order for the context menu |
|
| `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 |
|
| `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
|
### Per-Prompt API Override Properties
|
||||||
|
|
||||||
|
|
|
||||||
BIN
images/custom_menu/empty_icon.png
Normal file
BIN
images/custom_menu/empty_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 705 B |
|
|
@ -528,14 +528,18 @@ export class mzta_Menus {
|
||||||
// Create child menu items
|
// Create child menu items
|
||||||
for (const prompt of contextPrompts) {
|
for (const prompt of contextPrompts) {
|
||||||
const title = i18nConditionalGet(prompt.name);
|
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 =>
|
await new Promise(resolve =>
|
||||||
browser.menus.create({
|
browser.menus.create(menuOpts, resolve)
|
||||||
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");
|
this.logger.log("Context menus loaded: " + contextPrompts.length + " items");
|
||||||
|
|
|
||||||
|
|
@ -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.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.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.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{
|
}else{
|
||||||
prompt.position_display = pos;
|
prompt.position_display = pos;
|
||||||
prompt.position_compose = pos;
|
prompt.position_compose = pos;
|
||||||
|
|
@ -600,6 +601,9 @@ async function getCustomPrompts() {
|
||||||
if(prompt.show_in === undefined){
|
if(prompt.show_in === undefined){
|
||||||
prompt.show_in = "popup";
|
prompt.show_in = "popup";
|
||||||
}
|
}
|
||||||
|
if(prompt.custom_icon === undefined){
|
||||||
|
prompt.custom_icon = "";
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return prefs._custom_prompt;
|
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,
|
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,
|
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,
|
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));
|
//console.log('>>>>>>>>>>>>>> default_prompts_properties: ' + JSON.stringify(default_prompts_properties));
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,22 @@ export const specialPromptToContextMenuID = {
|
||||||
// const defaultContextMenuIcon = 'moz-extension:images/icon-32px.png';
|
// const defaultContextMenuIcon = 'moz-extension:images/icon-32px.png';
|
||||||
const defaultContextMenuIcon = '';
|
const defaultContextMenuIcon = '';
|
||||||
|
|
||||||
export function getContextMenuIcon(promptId) {
|
export function getContextMenuIcon(prompt) {
|
||||||
const contextMenuId = specialPromptToContextMenuID[promptId];
|
// Back-compat: accept a plain id string too
|
||||||
if (contextMenuId && contextMenuIconsPath[contextMenuId]) {
|
const promptId = (typeof prompt === 'string') ? prompt : prompt?.id;
|
||||||
return contextMenuIconsPath[contextMenuId];
|
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;
|
return defaultContextMenuIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
41
pages/menu_order/mzta-custom-menu-icons.js
Normal file
41
pages/menu_order/mzta-custom-menu-icons.js
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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',
|
||||||
|
];
|
||||||
|
|
@ -171,6 +171,86 @@
|
||||||
color: #e65100;
|
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 items */
|
||||||
.hidden_list .sortable_item {
|
.hidden_list .sortable_item {
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
|
|
@ -268,6 +348,29 @@
|
||||||
color: #ffb74d;
|
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 {
|
.hidden_list .sortable_item {
|
||||||
background: #2a2a2e;
|
background: #2a2a2e;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,17 @@ import {
|
||||||
setSpecialPrompts,
|
setSpecialPrompts,
|
||||||
getHiddenSpecialPromptIds
|
getHiddenSpecialPromptIds
|
||||||
} from '../../js/mzta-prompts.js';
|
} 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 allPrompts = [];
|
||||||
let allExcludedSpecialPrompts = []; // special prompts excluded from UI (hidden + inactive features), preserved on save
|
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';
|
handle.textContent = '\u2630';
|
||||||
li.appendChild(handle);
|
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
|
// Toggle checkbox
|
||||||
const toggle = document.createElement('input');
|
const toggle = document.createElement('input');
|
||||||
toggle.type = 'checkbox';
|
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 ====================
|
// ==================== Toggle show_in ====================
|
||||||
|
|
||||||
function toggleShowIn(prompt, menuType, isOn) {
|
function toggleShowIn(prompt, menuType, isOn) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue