mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-02-13 10:42:55 +01:00
Auto-executable QR
This commit is contained in:
parent
7841f3d91f
commit
e593dd4dbd
@ -3,19 +3,30 @@
|
|||||||
<h3><strong>Quick Reply Context Menu Editor</strong></h3>
|
<h3><strong>Quick Reply Context Menu Editor</strong></h3>
|
||||||
<div id="quickReply_contextMenuEditor_content">
|
<div id="quickReply_contextMenuEditor_content">
|
||||||
<template id="quickReply_contextMenuEditor_itemTemplate">
|
<template id="quickReply_contextMenuEditor_itemTemplate">
|
||||||
<div class="quickReplyContextMenuEditor_item flex-container alignitemscenter" data-order="0">
|
<div class="quickReplyContextMenuEditor_item flex-container alignitemscenter" data-order="0">
|
||||||
<span class="drag-handle ui-sortable-handle">☰</span>
|
<span class="drag-handle ui-sortable-handle">☰</span>
|
||||||
<select class="quickReply_contextMenuEditor_preset"></select>
|
<select class="quickReply_contextMenuEditor_preset"></select>
|
||||||
<label class="flex-container" title="When enabled, the current Quick Reply will be sent together with (before) the clicked QR from the context menu.">
|
<label class="flex-container" title="When enabled, the current Quick Reply will be sent together with (before) the clicked QR from the context menu.">
|
||||||
Chaining:
|
Chaining:
|
||||||
<input type="checkbox" class="quickReply_contextMenuEditor_chaining">
|
<input type="checkbox" class="quickReply_contextMenuEditor_chaining">
|
||||||
</label>
|
</label>
|
||||||
<span class="quickReply_contextMenuEditor_remove menu_button menu_button_icon fa-solid fa-trash-can" title="Remove entry"></span>
|
<span class="quickReply_contextMenuEditor_remove menu_button menu_button_icon fa-solid fa-trash-can" title="Remove entry"></span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="quickReply_contextMenuEditor_actions">
|
<div class="quickReply_contextMenuEditor_actions">
|
||||||
<span id="quickReply_contextMenuEditor_addPreset" class="menu_button menu_button_icon fa-solid fa-plus" title="Add preset to context menu"></span>
|
<span id="quickReply_contextMenuEditor_addPreset" class="menu_button menu_button_icon fa-solid fa-plus" title="Add preset to context menu"></span>
|
||||||
</div>
|
</div>
|
||||||
|
<h3><strong>Auto-Execute</strong></h3>
|
||||||
|
<div class="flex-container flexFlowColumn">
|
||||||
|
<label class="checkbox_label" for="quickReply_autoExecute_userMessage">
|
||||||
|
<input type="checkbox" id="quickReply_autoExecute_userMessage" >
|
||||||
|
<span><i class="fa-solid fa-fw fa-user"></i> Execute on user message</span>
|
||||||
|
</label>
|
||||||
|
<label class="checkbox_label" for="quickReply_autoExecute_botMessage">
|
||||||
|
<input type="checkbox" id="quickReply_autoExecute_botMessage" >
|
||||||
|
<span><i class="fa-solid fa-fw fa-robot"></i> Execute on AI message</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { saveSettingsDebounced, callPopup, getRequestHeaders, substituteParams } from "../../../script.js";
|
import { saveSettingsDebounced, callPopup, getRequestHeaders, substituteParams, eventSource, event_types } from "../../../script.js";
|
||||||
import { getContext, extension_settings } from "../../extensions.js";
|
import { getContext, extension_settings } from "../../extensions.js";
|
||||||
import { initScrollHeight, resetScrollHeight, getSortableDelay } from "../../utils.js";
|
import { initScrollHeight, resetScrollHeight, getSortableDelay } from "../../utils.js";
|
||||||
import { executeSlashCommands, registerSlashCommand } from "../../slash-commands.js";
|
import { executeSlashCommands, registerSlashCommand } from "../../slash-commands.js";
|
||||||
@ -113,7 +113,7 @@ async function onQuickReplyContextMenuChange(id) {
|
|||||||
|
|
||||||
async function onQuickReplyCtxButtonClick(id) {
|
async function onQuickReplyCtxButtonClick(id) {
|
||||||
const editorHtml = $(await $.get('scripts/extensions/quick-reply/contextMenuEditor.html'));
|
const editorHtml = $(await $.get('scripts/extensions/quick-reply/contextMenuEditor.html'));
|
||||||
const popupResult = callPopup(editorHtml, "confirm", undefined, { okButton: "Save", wide:false, large:false, rows: 1 });
|
const popupResult = callPopup(editorHtml, "confirm", undefined, { okButton: "Save", wide: false, large: false, rows: 1 });
|
||||||
const qr = extension_settings.quickReply.quickReplySlots[id - 1];
|
const qr = extension_settings.quickReply.quickReplySlots[id - 1];
|
||||||
if (!qr.contextMenu) {
|
if (!qr.contextMenu) {
|
||||||
qr.contextMenu = [];
|
qr.contextMenu = [];
|
||||||
@ -121,7 +121,7 @@ async function onQuickReplyCtxButtonClick(id) {
|
|||||||
/**@type {HTMLTemplateElement}*/
|
/**@type {HTMLTemplateElement}*/
|
||||||
const tpl = document.querySelector('#quickReply_contextMenuEditor_itemTemplate');
|
const tpl = document.querySelector('#quickReply_contextMenuEditor_itemTemplate');
|
||||||
const fillPresetSelect = (select, item) => {
|
const fillPresetSelect = (select, item) => {
|
||||||
[{name:'Select a preset', value:''}, ...presets].forEach(preset=>{
|
[{ name: 'Select a preset', value: '' }, ...presets].forEach(preset => {
|
||||||
const opt = document.createElement('option'); {
|
const opt = document.createElement('option'); {
|
||||||
opt.value = preset.value ?? preset.name;
|
opt.value = preset.value ?? preset.name;
|
||||||
opt.textContent = preset.name;
|
opt.textContent = preset.name;
|
||||||
@ -137,28 +137,43 @@ async function onQuickReplyCtxButtonClick(id) {
|
|||||||
const select = ctxItem.querySelector('.quickReply_contextMenuEditor_preset');
|
const select = ctxItem.querySelector('.quickReply_contextMenuEditor_preset');
|
||||||
fillPresetSelect(select, item);
|
fillPresetSelect(select, item);
|
||||||
dom.querySelector('.quickReply_contextMenuEditor_chaining').checked = item.chain;
|
dom.querySelector('.quickReply_contextMenuEditor_chaining').checked = item.chain;
|
||||||
$('.quickReply_contextMenuEditor_remove', ctxItem).on('click', ()=>ctxItem.remove());
|
$('.quickReply_contextMenuEditor_remove', ctxItem).on('click', () => ctxItem.remove());
|
||||||
document.querySelector('#quickReply_contextMenuEditor_content').append(ctxItem);
|
document.querySelector('#quickReply_contextMenuEditor_content').append(ctxItem);
|
||||||
}
|
}
|
||||||
[...qr.contextMenu, {}].forEach((item,idx)=>{
|
[...qr.contextMenu, {}].forEach((item, idx) => {
|
||||||
addCtxItem(item, idx)
|
addCtxItem(item, idx)
|
||||||
});
|
});
|
||||||
$('#quickReply_contextMenuEditor_addPreset').on('click', ()=>{
|
$('#quickReply_contextMenuEditor_addPreset').on('click', () => {
|
||||||
addCtxItem({}, document.querySelector('#quickReply_contextMenuEditor_content').children.length);
|
addCtxItem({}, document.querySelector('#quickReply_contextMenuEditor_content').children.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#quickReply_contextMenuEditor_content').sortable({
|
$('#quickReply_contextMenuEditor_content').sortable({
|
||||||
delay: getSortableDelay(),
|
delay: getSortableDelay(),
|
||||||
stop: ()=>{},
|
stop: () => { },
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#quickReply_autoExecute_userMessage').prop('checked', qr.autoExecute_userMessage ?? false);
|
||||||
|
$('#quickReply_autoExecute_botMessage').prop('checked', qr.autoExecute_botMessage ?? false);
|
||||||
|
|
||||||
|
$('#quickReply_autoExecute_userMessage').on('input', () => {
|
||||||
|
const state = !!$('#quickReply_autoExecute_userMessage').prop('checked');
|
||||||
|
qr.autoExecute_userMessage = state;
|
||||||
|
saveSettingsDebounced();
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#quickReply_autoExecute_botMessage').on('input', () => {
|
||||||
|
const state = !!$('#quickReply_autoExecute_botMessage').prop('checked');
|
||||||
|
qr.autoExecute_botMessage = state;
|
||||||
|
saveSettingsDebounced();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (await popupResult) {
|
if (await popupResult) {
|
||||||
qr.contextMenu = Array.from(document.querySelectorAll('#quickReply_contextMenuEditor_content > .quickReplyContextMenuEditor_item'))
|
qr.contextMenu = Array.from(document.querySelectorAll('#quickReply_contextMenuEditor_content > .quickReplyContextMenuEditor_item'))
|
||||||
.map(item=>({
|
.map(item => ({
|
||||||
preset: item.querySelector('.quickReply_contextMenuEditor_preset').value,
|
preset: item.querySelector('.quickReply_contextMenuEditor_preset').value,
|
||||||
chain: item.querySelector('.quickReply_contextMenuEditor_chaining').checked,
|
chain: item.querySelector('.quickReply_contextMenuEditor_chaining').checked,
|
||||||
}))
|
}))
|
||||||
.filter(item=>item.preset);
|
.filter(item => item.preset);
|
||||||
$(`#quickReplyContainer[data-order="${id}"]`).attr('data-contextMenu', JSON.stringify(qr.contextMenu));
|
$(`#quickReplyContainer[data-order="${id}"]`).attr('data-contextMenu', JSON.stringify(qr.contextMenu));
|
||||||
updateQuickReplyPreset();
|
updateQuickReplyPreset();
|
||||||
onQuickReplyLabelInput(id);
|
onQuickReplyLabelInput(id);
|
||||||
@ -234,30 +249,30 @@ async function performQuickReply(prompt, index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function buildContextMenu(qr, chainMes=null, hierarchy=[], labelHierarchy=[]) {
|
function buildContextMenu(qr, chainMes = null, hierarchy = [], labelHierarchy = []) {
|
||||||
const tree = {
|
const tree = {
|
||||||
label: qr.label,
|
label: qr.label,
|
||||||
mes: (chainMes&&qr.mes ? `${chainMes} | ` : '') + qr.mes,
|
mes: (chainMes && qr.mes ? `${chainMes} | ` : '') + qr.mes,
|
||||||
children: [],
|
children: [],
|
||||||
};
|
};
|
||||||
qr.contextMenu?.forEach(ctxItem=>{
|
qr.contextMenu?.forEach(ctxItem => {
|
||||||
let chain = ctxItem.chain;
|
let chain = ctxItem.chain;
|
||||||
let subName = ctxItem.preset;
|
let subName = ctxItem.preset;
|
||||||
const sub = presets.find(it=>it.name == subName);
|
const sub = presets.find(it => it.name == subName);
|
||||||
if (sub) {
|
if (sub) {
|
||||||
// prevent circular references
|
// prevent circular references
|
||||||
if (hierarchy.indexOf(sub.name) == -1) {
|
if (hierarchy.indexOf(sub.name) == -1) {
|
||||||
const nextHierarchy = [...hierarchy, sub.name];
|
const nextHierarchy = [...hierarchy, sub.name];
|
||||||
const nextLabelHierarchy = [...labelHierarchy, tree.label];
|
const nextLabelHierarchy = [...labelHierarchy, tree.label];
|
||||||
tree.children.push(new MenuHeader(sub.name));
|
tree.children.push(new MenuHeader(sub.name));
|
||||||
sub.quickReplySlots.forEach(subQr=>{
|
sub.quickReplySlots.forEach(subQr => {
|
||||||
const subInfo = buildContextMenu(subQr, chain?tree.mes:null, nextHierarchy, nextLabelHierarchy);
|
const subInfo = buildContextMenu(subQr, chain ? tree.mes : null, nextHierarchy, nextLabelHierarchy);
|
||||||
tree.children.push(new MenuItem(
|
tree.children.push(new MenuItem(
|
||||||
subInfo.label,
|
subInfo.label,
|
||||||
subInfo.mes,
|
subInfo.mes,
|
||||||
(evt)=>{
|
(evt) => {
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
performQuickReply(subInfo.mes.replace(/%%parent(-\d+)?%%/g, (_, index)=>{
|
performQuickReply(subInfo.mes.replace(/%%parent(-\d+)?%%/g, (_, index) => {
|
||||||
return nextLabelHierarchy.slice(parseInt(index ?? '-1'))[0];
|
return nextLabelHierarchy.slice(parseInt(index ?? '-1'))[0];
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
@ -476,7 +491,7 @@ function generateQuickReplyElements() {
|
|||||||
$(`#quickReply${i}Mes`).on('input', function () { onQuickReplyInput(i); });
|
$(`#quickReply${i}Mes`).on('input', function () { onQuickReplyInput(i); });
|
||||||
$(`#quickReply${i}Label`).on('input', function () { onQuickReplyLabelInput(i); });
|
$(`#quickReply${i}Label`).on('input', function () { onQuickReplyLabelInput(i); });
|
||||||
$(`#quickReply${i}CtxButton`).on('click', function () { onQuickReplyCtxButtonClick(i); });
|
$(`#quickReply${i}CtxButton`).on('click', function () { onQuickReplyCtxButtonClick(i); });
|
||||||
$(`#quickReplyContainer > [data-order="${i}"]`).attr('data-contextMenu', JSON.stringify(extension_settings.quickReply.quickReplySlots[i-1]?.contextMenu??[]));
|
$(`#quickReplyContainer > [data-order="${i}"]`).attr('data-contextMenu', JSON.stringify(extension_settings.quickReply.quickReplySlots[i - 1]?.contextMenu ?? []));
|
||||||
}
|
}
|
||||||
|
|
||||||
$('.quickReplySettings .inline-drawer-toggle').off('click').on('click', function () {
|
$('.quickReplySettings .inline-drawer-toggle').off('click').on('click', function () {
|
||||||
@ -545,6 +560,24 @@ function saveQROrder() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function onMessageReceived() {
|
||||||
|
for (let i = 0; i < extension_settings.quickReply.numberOfSlots; i++) {
|
||||||
|
const qr = extension_settings.quickReply.quickReplySlots[i];
|
||||||
|
if (qr?.autoExecute_botMessage) {
|
||||||
|
await sendQuickReply(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onMessageSent() {
|
||||||
|
for (let i = 0; i < extension_settings.quickReply.numberOfSlots; i++) {
|
||||||
|
const qr = extension_settings.quickReply.quickReplySlots[i];
|
||||||
|
if (qr?.autoExecute_userMessage) {
|
||||||
|
await sendQuickReply(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
jQuery(async () => {
|
jQuery(async () => {
|
||||||
moduleWorker();
|
moduleWorker();
|
||||||
setInterval(moduleWorker, UPDATE_INTERVAL);
|
setInterval(moduleWorker, UPDATE_INTERVAL);
|
||||||
@ -625,6 +658,9 @@ jQuery(async () => {
|
|||||||
|
|
||||||
await loadSettings('init');
|
await loadSettings('init');
|
||||||
addQuickReplyBar();
|
addQuickReplyBar();
|
||||||
|
|
||||||
|
eventSource.on(event_types.MESSAGE_RECEIVED, onMessageReceived);
|
||||||
|
eventSource.on(event_types.MESSAGE_SENT, onMessageSent);
|
||||||
});
|
});
|
||||||
|
|
||||||
jQuery(() => {
|
jQuery(() => {
|
||||||
|
@ -1,65 +1,65 @@
|
|||||||
import { MenuItem } from "./MenuItem.js";
|
import { MenuItem } from "./MenuItem.js";
|
||||||
|
|
||||||
export class ContextMenu {
|
export class ContextMenu {
|
||||||
/**@type {MenuItem[]}*/ itemList = [];
|
/**@type {MenuItem[]}*/ itemList = [];
|
||||||
/**@type {Boolean}*/ isActive = false;
|
/**@type {Boolean}*/ isActive = false;
|
||||||
|
|
||||||
/**@type {HTMLElement}*/ root;
|
/**@type {HTMLElement}*/ root;
|
||||||
/**@type {HTMLElement}*/ menu;
|
/**@type {HTMLElement}*/ menu;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
constructor(/**@type {MenuItem[]}*/items) {
|
constructor(/**@type {MenuItem[]}*/items) {
|
||||||
this.itemList = items;
|
this.itemList = items;
|
||||||
items.forEach(item=>{
|
items.forEach(item => {
|
||||||
item.onExpand = ()=>{
|
item.onExpand = () => {
|
||||||
items.filter(it=>it!=item)
|
items.filter(it => it != item)
|
||||||
.forEach(it=>it.collapse());
|
.forEach(it => it.collapse());
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.root) {
|
if (!this.root) {
|
||||||
const blocker = document.createElement('div'); {
|
const blocker = document.createElement('div'); {
|
||||||
this.root = blocker;
|
this.root = blocker;
|
||||||
blocker.classList.add('ctx-blocker');
|
blocker.classList.add('ctx-blocker');
|
||||||
blocker.addEventListener('click', ()=>this.hide());
|
blocker.addEventListener('click', () => this.hide());
|
||||||
const menu = document.createElement('ul'); {
|
const menu = document.createElement('ul'); {
|
||||||
this.menu = menu;
|
this.menu = menu;
|
||||||
menu.classList.add('list-group');
|
menu.classList.add('list-group');
|
||||||
menu.classList.add('ctx-menu');
|
menu.classList.add('ctx-menu');
|
||||||
this.itemList.forEach(it=>menu.append(it.render()));
|
this.itemList.forEach(it => menu.append(it.render()));
|
||||||
blocker.append(menu);
|
blocker.append(menu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.root;
|
return this.root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
show({clientX, clientY}) {
|
show({ clientX, clientY }) {
|
||||||
if (this.isActive) return;
|
if (this.isActive) return;
|
||||||
this.isActive = true;
|
this.isActive = true;
|
||||||
this.render();
|
this.render();
|
||||||
this.menu.style.bottom = `${window.innerHeight - clientY}px`;
|
this.menu.style.bottom = `${window.innerHeight - clientY}px`;
|
||||||
this.menu.style.left = `${clientX}px`;
|
this.menu.style.left = `${clientX}px`;
|
||||||
document.body.append(this.root);
|
document.body.append(this.root);
|
||||||
}
|
}
|
||||||
hide() {
|
hide() {
|
||||||
if (this.root) {
|
if (this.root) {
|
||||||
this.root.remove();
|
this.root.remove();
|
||||||
}
|
}
|
||||||
this.isActive = false;
|
this.isActive = false;
|
||||||
}
|
}
|
||||||
toggle(/**@type {PointerEvent}*/evt) {
|
toggle(/**@type {PointerEvent}*/evt) {
|
||||||
if (this.isActive) {
|
if (this.isActive) {
|
||||||
this.hide();
|
this.hide();
|
||||||
} else {
|
} else {
|
||||||
this.show(evt);
|
this.show(evt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,20 @@
|
|||||||
import { MenuItem } from "./MenuItem.js";
|
import { MenuItem } from "./MenuItem.js";
|
||||||
import { SubMenu } from "./SubMenu.js";
|
|
||||||
|
|
||||||
export class MenuHeader extends MenuItem {
|
export class MenuHeader extends MenuItem {
|
||||||
constructor(/**@type {String}*/label) {
|
constructor(/**@type {String}*/label) {
|
||||||
super(label, null, null);
|
super(label, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.root) {
|
if (!this.root) {
|
||||||
const item = document.createElement('li'); {
|
const item = document.createElement('li'); {
|
||||||
this.root = item;
|
this.root = item;
|
||||||
item.classList.add('list-group-item');
|
item.classList.add('list-group-item');
|
||||||
item.classList.add('ctx-header');
|
item.classList.add('ctx-header');
|
||||||
item.append(this.label);
|
item.append(this.label);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.root;
|
return this.root;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,76 +1,76 @@
|
|||||||
import { SubMenu } from "./SubMenu.js";
|
import { SubMenu } from "./SubMenu.js";
|
||||||
|
|
||||||
export class MenuItem {
|
export class MenuItem {
|
||||||
/**@type {String}*/ label;
|
/**@type {String}*/ label;
|
||||||
/**@type {Object}*/ value;
|
/**@type {Object}*/ value;
|
||||||
/**@type {Function}*/ callback;
|
/**@type {Function}*/ callback;
|
||||||
/**@type {MenuItem[]}*/ childList = [];
|
/**@type {MenuItem[]}*/ childList = [];
|
||||||
/**@type {SubMenu}*/ subMenu;
|
/**@type {SubMenu}*/ subMenu;
|
||||||
/**@type {Boolean}*/ isForceExpanded = false;
|
/**@type {Boolean}*/ isForceExpanded = false;
|
||||||
|
|
||||||
/**@type {HTMLElement}*/ root;
|
/**@type {HTMLElement}*/ root;
|
||||||
|
|
||||||
/**@type {Function}*/ onExpand;
|
/**@type {Function}*/ onExpand;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
constructor(/**@type {String}*/label, /**@type {Object}*/value, /**@type {function}*/callback, /**@type {MenuItem[]}*/children=[]) {
|
constructor(/**@type {String}*/label, /**@type {Object}*/value, /**@type {function}*/callback, /**@type {MenuItem[]}*/children = []) {
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.childList = children;
|
this.childList = children;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.root) {
|
if (!this.root) {
|
||||||
const item = document.createElement('li'); {
|
const item = document.createElement('li'); {
|
||||||
this.root = item;
|
this.root = item;
|
||||||
item.classList.add('list-group-item');
|
item.classList.add('list-group-item');
|
||||||
item.classList.add('ctx-item');
|
item.classList.add('ctx-item');
|
||||||
item.title = this.value;
|
item.title = this.value;
|
||||||
if (this.callback) {
|
if (this.callback) {
|
||||||
item.addEventListener('click', (evt)=>this.callback(evt, this));
|
item.addEventListener('click', (evt) => this.callback(evt, this));
|
||||||
}
|
}
|
||||||
item.append(this.label);
|
item.append(this.label);
|
||||||
if (this.childList.length > 0) {
|
if (this.childList.length > 0) {
|
||||||
item.classList.add('ctx-has-children');
|
item.classList.add('ctx-has-children');
|
||||||
const sub = new SubMenu(this.childList);
|
const sub = new SubMenu(this.childList);
|
||||||
this.subMenu = sub;
|
this.subMenu = sub;
|
||||||
const trigger = document.createElement('div'); {
|
const trigger = document.createElement('div'); {
|
||||||
trigger.classList.add('ctx-expander');
|
trigger.classList.add('ctx-expander');
|
||||||
trigger.textContent = '⋮';
|
trigger.textContent = '⋮';
|
||||||
trigger.addEventListener('click', (evt)=>{
|
trigger.addEventListener('click', (evt) => {
|
||||||
evt.stopPropagation();
|
evt.stopPropagation();
|
||||||
this.toggle();
|
this.toggle();
|
||||||
});
|
});
|
||||||
item.append(trigger);
|
item.append(trigger);
|
||||||
}
|
}
|
||||||
item.addEventListener('mouseover', ()=>sub.show(item));
|
item.addEventListener('mouseover', () => sub.show(item));
|
||||||
item.addEventListener('mouseleave', ()=>sub.hide());
|
item.addEventListener('mouseleave', () => sub.hide());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.root;
|
return this.root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
expand() {
|
expand() {
|
||||||
this.subMenu?.show(this.root);
|
this.subMenu?.show(this.root);
|
||||||
if (this.onExpand) {
|
if (this.onExpand) {
|
||||||
this.onExpand();
|
this.onExpand();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
collapse() {
|
collapse() {
|
||||||
this.subMenu?.hide();
|
this.subMenu?.hide();
|
||||||
}
|
}
|
||||||
toggle() {
|
toggle() {
|
||||||
if (this.subMenu.isActive) {
|
if (this.subMenu.isActive) {
|
||||||
this.expand();
|
this.expand();
|
||||||
} else {
|
} else {
|
||||||
this.collapse();
|
this.collapse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,64 +1,64 @@
|
|||||||
import { MenuItem } from "./MenuItem.js";
|
import { MenuItem } from "./MenuItem.js";
|
||||||
|
|
||||||
export class SubMenu {
|
export class SubMenu {
|
||||||
/**@type {MenuItem[]}*/ itemList = [];
|
/**@type {MenuItem[]}*/ itemList = [];
|
||||||
/**@type {Boolean}*/ isActive = false;
|
/**@type {Boolean}*/ isActive = false;
|
||||||
|
|
||||||
/**@type {HTMLElement}*/ root;
|
/**@type {HTMLElement}*/ root;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
constructor(/**@type {MenuItem[]}*/items) {
|
constructor(/**@type {MenuItem[]}*/items) {
|
||||||
this.itemList = items;
|
this.itemList = items;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.root) {
|
if (!this.root) {
|
||||||
const menu = document.createElement('ul'); {
|
const menu = document.createElement('ul'); {
|
||||||
this.root = menu;
|
this.root = menu;
|
||||||
menu.classList.add('list-group');
|
menu.classList.add('list-group');
|
||||||
menu.classList.add('ctx-menu');
|
menu.classList.add('ctx-menu');
|
||||||
menu.classList.add('ctx-sub-menu');
|
menu.classList.add('ctx-sub-menu');
|
||||||
this.itemList.forEach(it=>menu.append(it.render()));
|
this.itemList.forEach(it => menu.append(it.render()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.root;
|
return this.root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
show(/**@type {HTMLElement}*/parent) {
|
show(/**@type {HTMLElement}*/parent) {
|
||||||
if (this.isActive) return;
|
if (this.isActive) return;
|
||||||
this.isActive = true;
|
this.isActive = true;
|
||||||
this.render();
|
this.render();
|
||||||
parent.append(this.root);
|
parent.append(this.root);
|
||||||
requestAnimationFrame(()=>{
|
requestAnimationFrame(() => {
|
||||||
const rect = this.root.getBoundingClientRect();
|
const rect = this.root.getBoundingClientRect();
|
||||||
console.log(window.innerHeight, rect);
|
console.log(window.innerHeight, rect);
|
||||||
if (rect.bottom > window.innerHeight - 5) {
|
if (rect.bottom > window.innerHeight - 5) {
|
||||||
this.root.style.top = `${window.innerHeight - 5 - rect.bottom}px`;
|
this.root.style.top = `${window.innerHeight - 5 - rect.bottom}px`;
|
||||||
}
|
}
|
||||||
if (rect.right > window.innerWidth - 5) {
|
if (rect.right > window.innerWidth - 5) {
|
||||||
this.root.style.left = 'unset';
|
this.root.style.left = 'unset';
|
||||||
this.root.style.right = '100%';
|
this.root.style.right = '100%';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
hide() {
|
hide() {
|
||||||
if (this.root) {
|
if (this.root) {
|
||||||
this.root.remove();
|
this.root.remove();
|
||||||
this.root.style.top = '';
|
this.root.style.top = '';
|
||||||
this.root.style.left = '';
|
this.root.style.left = '';
|
||||||
}
|
}
|
||||||
this.isActive = false;
|
this.isActive = false;
|
||||||
}
|
}
|
||||||
toggle(/**@type {HTMLElement}*/parent) {
|
toggle(/**@type {HTMLElement}*/parent) {
|
||||||
if (this.isActive) {
|
if (this.isActive) {
|
||||||
this.hide();
|
this.hide();
|
||||||
} else {
|
} else {
|
||||||
this.show(parent);
|
this.show(parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { chat_metadata, getCurrentChatId, sendSystemMessage, system_message_types } from "../script.js";
|
import { chat_metadata, getCurrentChatId, saveSettingsDebounced, sendSystemMessage, system_message_types } from "../script.js";
|
||||||
import { extension_settings } from "./extensions.js";
|
import { extension_settings, saveMetadataDebounced } from "./extensions.js";
|
||||||
import { executeSlashCommands, registerSlashCommand } from "./slash-commands.js";
|
import { executeSlashCommands, registerSlashCommand } from "./slash-commands.js";
|
||||||
|
|
||||||
function getLocalVariable(name) {
|
function getLocalVariable(name) {
|
||||||
@ -18,6 +18,7 @@ function setLocalVariable(name, value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
chat_metadata.variables[name] = value;
|
chat_metadata.variables[name] = value;
|
||||||
|
saveMetadataDebounced();
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ function getGlobalVariable(name) {
|
|||||||
|
|
||||||
function setGlobalVariable(name, value) {
|
function setGlobalVariable(name, value) {
|
||||||
extension_settings.variables.global[name] = value;
|
extension_settings.variables.global[name] = value;
|
||||||
|
saveSettingsDebounced();
|
||||||
}
|
}
|
||||||
|
|
||||||
function addLocalVariable(name, value) {
|
function addLocalVariable(name, value) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user