Chore: Add code formatting conventions as eslint rules (#5158)

* Add code formatting conventions as eslint rules

* Improve formatting in addQuickReply
This commit is contained in:
Cohee
2026-02-15 01:16:34 +02:00
committed by GitHub
parent d118eee014
commit 357da3219b
94 changed files with 366 additions and 566 deletions
+23
View File
@@ -102,5 +102,28 @@ module.exports = {
// These rules should eventually be enabled.
'no-async-promise-executor': 'off',
'no-inner-declarations': 'off',
'brace-style': 'off',
// Additional formatting rules based on codebase conventions
'array-bracket-spacing': ['error', 'never'],
'computed-property-spacing': ['error', 'never'],
'block-spacing': ['error', 'always'],
'keyword-spacing': ['error', { before: true, after: true }],
'space-before-blocks': ['error', 'always'],
'space-before-function-paren': ['error', { anonymous: 'always', named: 'never', asyncArrow: 'always' }],
'space-in-parens': ['error', 'never'],
'comma-spacing': ['error', { before: false, after: true }],
'key-spacing': ['error', { beforeColon: false, afterColon: true }],
'func-call-spacing': ['error', 'never'],
'no-multiple-empty-lines': ['error', { max: 2, maxEOF: 1, maxBOF: 0 }],
'padded-blocks': ['error', 'never'],
'no-whitespace-before-property': 'error',
'space-unary-ops': ['error', { words: true, nonwords: false }],
'arrow-spacing': ['error', { before: true, after: true }],
'template-curly-spacing': ['error', 'never'],
'rest-spread-spacing': ['error', 'never'],
'generator-star-spacing': ['error', { before: false, after: true }],
'yield-star-spacing': ['error', { before: false, after: true }],
'template-tag-spacing': ['error', 'never'],
'switch-colon-spacing': ['error', { after: true, before: false }],
},
};
+2 -14
View File
@@ -2516,7 +2516,6 @@ export function addOneMessage(mes, { type = undefined, insertAfter = null, scrol
* @returns {JQuery<HTMLElement>} Rendered HTMLElement.
*/
export function updateMessageElement(mes, { messageId = chat.length - 1, messageElement = messageTemplate.clone(), adjustMediaScroll = SCROLL_BEHAVIOR.NONE } = {}) {
let avatarImg = getThumbnailUrl('persona', user_avatar);
//for non-user messages
@@ -3709,9 +3708,9 @@ class StreamingProcessor {
}
/**
* @returns {Generator<{ text: string, swipes: string[], logprobs: import('./scripts/logprobs.js').TokenLogprobs, toolCalls: any[], state: any }, void, void>}
* @returns {AsyncGenerator<{ text: string, swipes: string[], logprobs: import('./scripts/logprobs.js').TokenLogprobs, toolCalls: any[], state: any }, void, void>}
*/
*nullStreamingGeneration() {
async* nullStreamingGeneration() {
throw new Error('Generation function for streaming is not hooked up');
}
@@ -4862,7 +4861,6 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
// Add quiet generation prompt at depth 0
if (quiet_prompt && quiet_prompt.length) {
// here name1 is forced for all quiet prompts..why?
const name = name1;
//checks if we are in instruct, if so, formats the chat as such, otherwise just adds the quiet prompt
@@ -5399,7 +5397,6 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
if (!isAborted && power_user.auto_swipe && generatedTextFiltered(getMessage)) {
is_send_press = false;
return await swipe(null, SWIPE_DIRECTION.RIGHT, { source: SWIPE_SOURCE.AUTO_SWIPE, repeated: true, forceMesId: chat.length - 1 });
}
console.debug('/api/chats/save called by /Generate');
@@ -6517,7 +6514,6 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title
!fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type);
addOneMessage(chat[chat_id], { type: 'swipe' });
!fromStreaming && await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, chat_id, type);
} else {
console.debug('entering chat update routine for non-swipe post');
const newMessage = {};
@@ -8247,7 +8243,6 @@ export async function getChatsFromFiles(data, isGroupChat) {
currentChat.shift();
}
chat_dict[file_name] = currentChat;
} catch (error) {
console.error(error);
}
@@ -9588,7 +9583,6 @@ export async function createOrEditCharacter(e) {
select_rm_info('char_create', avatarId, oldSelectedChar);
crop_data = undefined;
} catch (error) {
console.error('Error creating character', error);
toastr.error(t`Failed to create character`);
@@ -9984,7 +9978,6 @@ export async function swipe(event, direction, { source, repeated, message = chat
duration: 0, //used to be 100 //Disabled on Cohee's request. https://github.com/SillyTavern/SillyTavern/pull/4610/files#r2408731744
queue: false,
progress: function (animation, progress, remainingMs) {
if (is_animation_scroll) chatElement.scrollTop(getMessageBottomHeight(thisMesDiv));
},
complete: function () {
@@ -10001,7 +9994,6 @@ export async function swipe(event, direction, { source, repeated, message = chat
* @param {boolean} [skipSwipeOut=false]
*/
async function animateSwipe(run_generate = false, skipSwipeOut = false) {
if (!skipSwipeOut) {
//Swipe out.
await animateSwipeTransition(mesId, { xEnd: `${swipeRange}px`, duration: swipeDuration });
@@ -10069,7 +10061,6 @@ export async function swipe(event, direction, { source, repeated, message = chat
//If the swipe is not being deleted.
if (source != SWIPE_SOURCE.DELETE && source != SWIPE_SOURCE.BACK) {
// Make sure ad-hoc changes to extras are saved before swiping away
syncMesToSwipe(mesId);
@@ -10382,7 +10373,6 @@ export async function doNewChat({ deleteCurrentChat = false } = {}) {
await createOrEditCharacter(new CustomEvent('newChat'));
if (deleteCurrentChat) await delChat(chat_file_for_del + '.jsonl');
}
}
/**
@@ -11407,9 +11397,7 @@ jQuery(async function () {
divchat.style.borderRadius = '';
divchat.style.backgroundColor = '';
} else {
divchat.style.borderRadius = '10px'; // Adjust the value to control the roundness of the corners
divchat.style.backgroundColor = ''; // Set the background color to your preference
+2 -2
View File
@@ -101,7 +101,7 @@ class CharacterContextMenu {
* @param {number} characterId
* @returns {Promise<void>}
*/
static persona = async (characterId) => void(await convertCharacterToPersona(characterId));
static persona = async (characterId) => void (await convertCharacterToPersona(characterId));
/**
* Delete one or more characters,
@@ -754,7 +754,7 @@ class BulkEditOverlay {
handleContextMenuShow = (event) => {
event.preventDefault();
const [x,y] = this.#getContextMenuPosition(event);
const [x, y] = this.#getContextMenuPosition(event);
CharacterContextMenu.show(x, y);
this.#contextMenuOpen = true;
};
-2
View File
@@ -986,7 +986,6 @@ class PromptManager {
* @returns {void}
*/
addPrompt(prompt, identifier) {
if (typeof prompt !== 'object' || prompt === null) throw new Error('Object is not a prompt');
const newPrompt = {
@@ -1318,7 +1317,6 @@ class PromptManager {
this.updatePromptByIdentifier(identifier, prompt);
debouncedSaveServiceSettings().then(() => this.render());
});
}
/**
-3
View File
@@ -101,7 +101,6 @@ observer.observe(document.documentElement, observerConfig);
* @returns {string} - A human-readable string that represents the time spent generating characters.
*/
export function humanizeGenTime(total_gen_time) {
//convert time_spent to humanized format of "_ Hours, _ Minutes, _ Seconds" from milliseconds
let time_spent = total_gen_time || 0;
time_spent = Math.floor(time_spent / 1000);
@@ -1274,8 +1273,6 @@ export function initRossMods() {
}
if (event.ctrlKey && /^[1-9]$/.test(event.key)) {
// This will eventually be to trigger quick replies
// event.preventDefault();
@@ -80,8 +80,6 @@ export class AutoComplete {
}
/**
* @param {HTMLTextAreaElement|HTMLInputElement} textarea The textarea to receive autocomplete.
* @param {() => boolean} checkIfActivate Function should return true only if under the current conditions, autocomplete should display (e.g., for slash commands: autoComplete.text[0] == '/')
@@ -414,7 +412,6 @@ export class AutoComplete {
});
if (this.isForceHidden) {
// hidden with escape
return this.hide();
@@ -465,7 +462,6 @@ export class AutoComplete {
}
/**
* Create updated DOM.
*/
@@ -514,7 +510,6 @@ export class AutoComplete {
}
/**
* Update position of DOM.
*/
@@ -1,6 +1,3 @@
export class AutoCompleteFuzzyScore {
/**@type {number}*/ start;
/**@type {number}*/ longestConsecutive;
@@ -2,7 +2,6 @@ import { AutoCompleteNameResultBase } from './AutoCompleteNameResultBase.js';
import { AutoCompleteSecondaryNameResult } from './AutoCompleteSecondaryNameResult.js';
export class AutoCompleteNameResult extends AutoCompleteNameResultBase {
/**
*
@@ -1,14 +1,13 @@
import { AutoCompleteOption } from './AutoCompleteOption.js';
export class AutoCompleteNameResultBase {
/**@type {string} */ name;
/**@type {number} */ start;
/**@type {AutoCompleteOption[]} */ optionList = [];
/**@type {boolean} */ canBeQuoted = false;
/**@type {()=>string} */ makeNoMatchText = ()=>`No matches found for "${this.name}"`;
/**@type {()=>string} */ makeNoOptionsText = ()=>'No options';
/**@type {()=>string} */ makeNoMatchText = () => `No matches found for "${this.name}"`;
/**@type {()=>string} */ makeNoOptionsText = () => 'No options';
/**
@@ -1,7 +1,6 @@
import { AutoCompleteFuzzyScore } from './AutoCompleteFuzzyScore.js';
export class AutoCompleteOption {
/** @type {string} */ name;
/** @type {string} */ typeIcon;
@@ -72,7 +71,7 @@ export class AutoCompleteOption {
name.classList.add('name');
name.classList.add('monospace');
name.textContent = noSlash ? '' : '/';
key.split('').forEach(char=>{
key.split('').forEach(char => {
const span = document.createElement('span'); {
span.textContent = char;
name.append(span);
+1 -1
View File
@@ -55,7 +55,7 @@ function onSelectAllButtonClick() {
if (!atLeastOneSelected) {
// If none was selected, trigger click on all to deselect all of them
for(const character of characters) {
for (const character of characters) {
const checked = $(character).find('.bulk_select_checkbox:checked') ?? false;
if (checked && character instanceof HTMLElement) {
characterGroupOverlay.toggleSingleCharacter(character);
-1
View File
@@ -150,7 +150,6 @@ function onCfgMenuItemClick() {
setTimeout(function () {
$('#cfgConfig').hide();
}, animation_duration);
}
//duplicate options menu close handler from script.js
//because this listener takes priority
+1 -1
View File
@@ -199,7 +199,7 @@ class BackupsBrowser {
const deleteButton = document.createElement('div');
deleteButton.classList.add('right_menu_button', 'fa-solid', 'fa-trash');
deleteButton.title = t`Delete backup`;
deleteButton.addEventListener('click',async () => {
deleteButton.addEventListener('click', async () => {
const isDeleted = await this.deleteBackup(backup.file_name);
if (isDeleted) {
listItem.remove();
-1
View File
@@ -251,7 +251,6 @@ class DataMaidDialog {
categoryElement.remove();
this.displayEmptyPlaceholder();
});
});
categoryElement.querySelectorAll('.dataMaidItemDelete').forEach(button => {
button.addEventListener('click', async () => {
+1 -2
View File
@@ -94,8 +94,7 @@ async function downloadAssetsList(url) {
updateCurrentAssets().then(async function () {
fetch(url, { cache: 'no-cache' })
.then(response => response.json())
.then(async function(json) {
.then(async function (json) {
availableAssets = {};
$('#assets_menu').empty();
@@ -1414,7 +1414,6 @@ export async function getExpressionsList({ filterAvailable = false } = {}) {
});
if (apiResult.ok) {
const data = await apiResult.json();
expressionsList = data.labels;
return expressionsList;
@@ -1488,7 +1487,6 @@ function chooseSpriteForExpression(spriteFolderName, expression, { prevExpressio
}
return spriteFile;
}
/**
@@ -2333,7 +2331,7 @@ function migrateSettings() {
name: 'expression-folder-override',
aliases: ['spriteoverride', 'costume'],
callback: setSpriteFolderCommand,
namedArgumentList:[
namedArgumentList: [
SlashCommandNamedArgument.fromProps({
name: 'name',
description: 'Character name to set a subfolder for. If not provided, the character who last sent a message will be used.',
@@ -791,7 +791,6 @@ async function listGalleryCommand(args) {
const items = await getGalleryItems(url);
return JSON.stringify(items.map(it => it.src));
} catch (err) {
console.error(err);
}
@@ -10,22 +10,18 @@ export class QuickReplyApi {
/** @type {SettingsUi} */ settingsUi;
constructor(/** @type {QuickReplySettings} */settings, /** @type {SettingsUi} */settingsUi) {
this.settings = settings;
this.settingsUi = settingsUi;
}
/**
* @param {QuickReply} qr
* @returns {QuickReplySet}
*/
getSetByQr(qr) {
return QuickReplySet.list.find(it=>it.qrList.includes(qr));
return QuickReplySet.list.find(it => it.qrList.includes(qr));
}
/**
@@ -48,13 +44,11 @@ export class QuickReplyApi {
getQrByLabel(setName, label) {
const set = this.getSetByName(setName);
if (!set) return;
if (Number.isInteger(label)) return set.qrList.find(it=>it.id == label);
return set.qrList.find(it=>it.label == label);
if (Number.isInteger(label)) return set.qrList.find(it => it.id == label);
return set.qrList.find(it => it.label == label);
}
/**
* Executes a quick reply by its index and returns the result.
*
@@ -63,7 +57,7 @@ export class QuickReplyApi {
*/
async executeQuickReplyByIndex(idx) {
const qr = [...this.settings.config.setList, ...(this.settings.chatConfig?.setList ?? [])]
.map(it=>it.set.qrList)
.map(it => it.set.qrList)
.flat()[idx]
;
if (qr) {
@@ -400,7 +394,7 @@ export class QuickReplyApi {
if (oldSet) {
QuickReplySet.list.splice(QuickReplySet.list.indexOf(oldSet), 1, set);
} else {
const idx = QuickReplySet.list.findIndex(it=>it.name.localeCompare(name) == 1);
const idx = QuickReplySet.list.findIndex(it => it.name.localeCompare(name) == 1);
if (idx > -1) {
QuickReplySet.list.splice(idx, 0, set);
} else {
@@ -460,7 +454,7 @@ export class QuickReplyApi {
* @returns array with the names of all quick reply sets
*/
listSets() {
return QuickReplySet.list.map(it=>it.name);
return QuickReplySet.list.map(it => it.name);
}
/**
* Gets a list of all globally active quick reply sets.
@@ -468,7 +462,7 @@ export class QuickReplyApi {
* @returns array with the names of all quick reply sets
*/
listGlobalSets() {
return this.settings.config.setList.map(it=>it.set.name);
return this.settings.config.setList.map(it => it.set.name);
}
/**
* Gets a list of all quick reply sets activated by the current chat.
@@ -476,7 +470,7 @@ export class QuickReplyApi {
* @returns array with the names of all quick reply sets
*/
listChatSets() {
return this.settings.chatConfig?.setList?.flatMap(it=>it.set.name) ?? [];
return this.settings.chatConfig?.setList?.flatMap(it => it.set.name) ?? [];
}
/**
@@ -490,7 +484,7 @@ export class QuickReplyApi {
if (!set) {
throw new Error(`No quick reply set with name "${name}" found.`);
}
return set.qrList.map(it=>it.label);
return set.qrList.map(it => it.label);
}
/**
+13 -17
View File
@@ -14,8 +14,6 @@ import { selected_group } from '../../group-chats.js';
export { debounceAsync };
const _VERBOSE = true;
export const debug = (...msg) => _VERBOSE ? console.debug('[QR2]', ...msg) : null;
export const log = (...msg) => _VERBOSE ? console.log('[QR2]', ...msg) : null;
@@ -54,8 +52,6 @@ let autoExec;
export let quickReplyApi;
const loadSets = async () => {
const response = await fetch('/api/settings/get', {
method: 'POST',
@@ -72,7 +68,7 @@ const loadSets = async () => {
set.disableSend = set.quickActionEnabled ?? false;
set.placeBeforeInput = set.placeBeforeInputEnabled ?? false;
set.injectInput = set.AutoInputInject ?? false;
set.qrList = set.quickReplySlots.map((slot,idx)=>{
set.qrList = set.quickReplySlots.map((slot, idx) => {
const qr = {};
qr.id = idx + 1;
qr.label = slot.label ?? '';
@@ -87,7 +83,7 @@ const loadSets = async () => {
qr.executeOnNewChat = slot.autoExecute_newChat ?? false;
qr.executeBeforeGeneration = slot.autoExecute_beforeGeneration ?? false;
qr.automationId = slot.automationId ?? '';
qr.contextList = (slot.contextMenu ?? []).map(it=>({
qr.contextList = (slot.contextMenu ?? []).map(it => ({
set: it.preset,
isChained: it.chain,
}));
@@ -99,8 +95,8 @@ const loadSets = async () => {
}
}
// need to load QR lists after all sets are loaded to be able to resolve context menu entries
setList.forEach((set, idx)=>{
QuickReplySet.list[idx].qrList = set.qrList.map(it=>QuickReply.from(it));
setList.forEach((set, idx) => {
QuickReplySet.list[idx].qrList = set.qrList.map(it => QuickReply.from(it));
QuickReplySet.list[idx].init();
});
log('sets: ', QuickReplySet.list);
@@ -140,7 +136,7 @@ const executeIfReadyElseQueue = async (functionToCall, args) => {
await functionToCall(...args);
} else {
log('queueing', { functionToCall, args });
executeQueue.push(async()=>await functionToCall(...args));
executeQueue.push(async () => await functionToCall(...args));
}
};
@@ -183,9 +179,9 @@ const init = async () => {
buttons = new ButtonUi(settings);
buttons.show();
settings.onSave = ()=>buttons.refresh();
settings.onSave = () => buttons.refresh();
globalThis.executeQuickReplyByName = async(name, args = {}, options = {}) => {
globalThis.executeQuickReplyByName = async (name, args = {}, options = {}) => {
let qr = [
...settings.config.setList,
...(settings.chatConfig?.setList ?? []),
@@ -193,14 +189,14 @@ const init = async () => {
]
.map(it => it.set.qrList)
.flat()
.find(it=>it.label == name)
.find(it => it.label == name)
;
if (!qr) {
let [setName, ...qrName] = name.split('.');
qrName = qrName.join('.');
let qrs = QuickReplySet.get(setName);
if (qrs) {
qr = qrs.qrList.find(it=>it.label == qrName);
qr = qrs.qrList.find(it => it.label == qrName);
}
}
if (qr && qr.onExecute) {
@@ -215,7 +211,7 @@ const init = async () => {
slash.init();
autoExec = new AutoExecuteHandler(settings);
eventSource.on(event_types.APP_READY, async()=>await finalizeInit());
eventSource.on(event_types.APP_READY, async () => await finalizeInit());
globalThis.quickReplyApi = quickReplyApi;
};
@@ -275,14 +271,14 @@ const onChatChanged = async (chatIdx) => {
await autoExec.handleChatChanged();
};
eventSource.on(event_types.CHAT_CHANGED, (...args)=>executeIfReadyElseQueue(onChatChanged, args));
eventSource.on(event_types.CHAT_CHANGED, (...args) => executeIfReadyElseQueue(onChatChanged, args));
eventSource.on(event_types.CHARACTER_DELETED, purgeCharacterQuickReplySets);
eventSource.on(event_types.CHARACTER_RENAMED, updateCharacterQuickReplySets);
const onUserMessage = async () => {
await autoExec.handleUser();
};
eventSource.makeFirst(event_types.USER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onUserMessage, args));
eventSource.makeFirst(event_types.USER_MESSAGE_RENDERED, (...args) => executeIfReadyElseQueue(onUserMessage, args));
const onAiMessage = async (messageId) => {
if (['...'].includes(chat[messageId]?.mes)) {
@@ -292,7 +288,7 @@ const onAiMessage = async (messageId) => {
await autoExec.handleAi();
};
eventSource.makeFirst(event_types.CHARACTER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onAiMessage, args));
eventSource.makeFirst(event_types.CHARACTER_MESSAGE_RENDERED, (...args) => executeIfReadyElseQueue(onAiMessage, args));
const onGroupMemberDraft = async () => {
await autoExec.handleGroupMemberDraft();
@@ -8,8 +8,6 @@ export class AutoExecuteHandler {
/** @type {Boolean[]}*/ preventAutoExecuteStack = [];
constructor(/** @type {QuickReplySettings} */settings) {
this.settings = settings;
}
@@ -20,13 +18,11 @@ export class AutoExecuteHandler {
}
async performAutoExecute(/** @type {QuickReply[]} */qrList) {
for (const qr of qrList) {
this.preventAutoExecuteStack.push(qr.preventAutoExecute);
try {
await qr.execute({ isAutoExecute:true });
await qr.execute({ isAutoExecute: true });
} catch (ex) {
warn(ex);
} finally {
@@ -22,13 +22,11 @@ export class QuickReply {
* @param {{ id?: number; contextList?: any; }} props
*/
static from(props) {
props.contextList = (props.contextList ?? []).map((/** @type {any} */ it)=>QuickReplyContextLink.from(it));
props.contextList = (props.contextList ?? []).map((/** @type {any} */ it) => QuickReplyContextLink.from(it));
return Object.assign(new this(), props);
}
/**@type {number}*/ id;
/**@type {string}*/ icon;
/**@type {string}*/ label = '';
@@ -89,8 +87,6 @@ export class QuickReply {
}
unrender() {
this.dom?.remove();
this.dom = null;
@@ -129,7 +125,7 @@ export class QuickReply {
menu.show(evt);
}
});
root.addEventListener('click', (evt)=>{
root.addEventListener('click', (evt) => {
if (evt.ctrlKey) {
this.showEditor();
return;
@@ -169,8 +165,6 @@ export class QuickReply {
}
renderSettings(idx) {
if (!this.settingsDom) {
const item = document.createElement('div'); {
@@ -190,7 +184,7 @@ export class QuickReply {
addNew.classList.add('fa-solid');
addNew.classList.add('fa-plus');
addNew.title = 'Add quick reply';
addNew.addEventListener('click', ()=>this.onInsertBefore());
addNew.addEventListener('click', () => this.onInsertBefore());
actions.append(addNew);
}
const paste = document.createElement('div'); {
@@ -201,7 +195,7 @@ export class QuickReply {
paste.classList.add('fa-solid');
paste.classList.add('fa-paste');
paste.title = 'Add quick reply from clipboard';
paste.addEventListener('click', async()=>{
paste.addEventListener('click', async () => {
const text = await navigator.clipboard.readText();
this.onInsertBefore(text);
});
@@ -215,11 +209,11 @@ export class QuickReply {
importFile.classList.add('fa-solid');
importFile.classList.add('fa-file-import');
importFile.title = 'Add quick reply from JSON file';
importFile.addEventListener('click', async()=>{
importFile.addEventListener('click', async () => {
const inp = document.createElement('input'); {
inp.type = 'file';
inp.accept = '.json';
inp.addEventListener('change', async()=>{
inp.addEventListener('change', async () => {
if (inp.files.length > 0) {
for (const file of inp.files) {
const text = await file.text();
@@ -256,7 +250,7 @@ export class QuickReply {
icon.classList.add('fa-solid');
icon.classList.add(this.icon);
}
icon.addEventListener('click', async()=>{
icon.addEventListener('click', async () => {
let value = await showFontAwesomePicker();
this.updateIcon(value);
});
@@ -267,7 +261,7 @@ export class QuickReply {
lbl.classList.add('qr--set-itemLabel');
lbl.classList.add('text_pole');
lbl.value = this.label;
lbl.addEventListener('input', ()=>this.updateLabel(lbl.value));
lbl.addEventListener('input', () => this.updateLabel(lbl.value));
lblContainer.append(lbl);
}
itemContent.append(lblContainer);
@@ -283,7 +277,7 @@ export class QuickReply {
opt.classList.add('fa-solid');
opt.textContent = '⁝';
opt.title = 'Additional options:\n - large editor\n - context menu\n - auto-execution\n - tooltip';
opt.addEventListener('click', ()=>this.showEditor());
opt.addEventListener('click', () => this.showEditor());
optContainer.append(opt);
}
itemContent.append(optContainer);
@@ -294,7 +288,7 @@ export class QuickReply {
mes.classList.add('qr--set-itemMessage');
mes.value = this.message;
//HACK need to use jQuery to catch the triggered event from the expanded editor
$(mes).on('input', ()=>this.updateMessage(mes.value));
$(mes).on('input', () => this.updateMessage(mes.value));
itemContent.append(mes);
}
const actions = document.createElement('div'); {
@@ -306,7 +300,7 @@ export class QuickReply {
move.classList.add('fa-solid');
move.classList.add('fa-truck-arrow-right');
move.title = 'Move quick reply to other set';
move.addEventListener('click', ()=>this.onTransfer(this));
move.addEventListener('click', () => this.onTransfer(this));
actions.append(move);
}
const copy = document.createElement('div'); {
@@ -316,7 +310,7 @@ export class QuickReply {
copy.classList.add('fa-solid');
copy.classList.add('fa-copy');
copy.title = 'Copy quick reply to clipboard';
copy.addEventListener('click', async()=>{
copy.addEventListener('click', async () => {
await navigator.clipboard.writeText(JSON.stringify(this));
copy.classList.add('qr--success');
await delay(3010);
@@ -331,7 +325,7 @@ export class QuickReply {
cut.classList.add('fa-solid');
cut.classList.add('fa-cut');
cut.title = 'Cut quick reply to clipboard (copy and remove)';
cut.addEventListener('click', async()=>{
cut.addEventListener('click', async () => {
await navigator.clipboard.writeText(JSON.stringify(this));
this.delete();
});
@@ -344,8 +338,8 @@ export class QuickReply {
exp.classList.add('fa-solid');
exp.classList.add('fa-file-export');
exp.title = 'Export quick reply as file';
exp.addEventListener('click', ()=>{
const blob = new Blob([JSON.stringify(this)], { type:'text' });
exp.addEventListener('click', () => {
const blob = new Blob([JSON.stringify(this)], { type: 'text' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a'); {
a.href = url;
@@ -363,7 +357,7 @@ export class QuickReply {
del.classList.add('fa-trash-can');
del.classList.add('redWarningBG');
del.title = 'Remove Quick Reply\n---\nShift+Click to skip confirmation';
del.addEventListener('click', async(evt)=>{
del.addEventListener('click', async (evt) => {
if (!evt.shiftKey) {
const result = await Popup.show.confirm(
'Remove Quick Reply',
@@ -408,7 +402,7 @@ export class QuickReply {
else {
icon.textContent = '…';
}
icon.addEventListener('click', async()=>{
icon.addEventListener('click', async () => {
let value = await showFontAwesomePicker();
if (value === null) return;
if (this.icon) icon.classList.remove(this.icon);
@@ -425,18 +419,18 @@ export class QuickReply {
/**@type {HTMLInputElement}*/
const showLabel = dom.querySelector('#qr--modal-showLabel');
showLabel.checked = this.showLabel;
showLabel.addEventListener('click', ()=>{
showLabel.addEventListener('click', () => {
this.updateShowLabel(showLabel.checked);
});
/**@type {HTMLInputElement}*/
const label = dom.querySelector('#qr--modal-label');
label.value = this.label;
label.addEventListener('input', ()=>{
label.addEventListener('input', () => {
this.updateLabel(label.value);
});
let switcherList;
// @ts-ignore
dom.querySelector('#qr--modal-switcher').addEventListener('click', (evt)=>{
dom.querySelector('#qr--modal-switcher').addEventListener('click', (evt) => {
if (switcherList) {
switcherList.remove();
switcherList = null;
@@ -445,15 +439,15 @@ export class QuickReply {
const list = document.createElement('ul'); {
switcherList = list;
list.classList.add('qr--modal-switcherList');
const makeList = (qrs)=>{
const makeList = (qrs) => {
const setItem = document.createElement('li'); {
setItem.classList.add('qr--modal-switcherItem');
setItem.addEventListener('click', ()=>{
setItem.addEventListener('click', () => {
list.innerHTML = '';
for (const qrs of quickReplyApi.listSets()) {
const item = document.createElement('li'); {
item.classList.add('qr--modal-switcherItem');
item.addEventListener('click', ()=>{
item.addEventListener('click', () => {
list.innerHTML = '';
makeList(quickReplyApi.getSetByName(qrs));
});
@@ -484,7 +478,7 @@ export class QuickReply {
}
const addItem = document.createElement('li'); {
addItem.classList.add('qr--modal-switcherItem');
addItem.addEventListener('click', ()=>{
addItem.addEventListener('click', () => {
const qr = quickReplyApi.getSetByQr(this).addQuickReply();
this.editorPopup.completeAffirmative();
qr.showEditor();
@@ -505,11 +499,11 @@ export class QuickReply {
}
list.append(addItem);
}
for (const qr of qrs.qrList.toSorted((a,b)=>a.label.toLowerCase().localeCompare(b.label.toLowerCase()))) {
for (const qr of qrs.qrList.toSorted((a, b) => a.label.toLowerCase().localeCompare(b.label.toLowerCase()))) {
const item = document.createElement('li'); {
item.classList.add('qr--modal-switcherItem');
if (qr == this) item.classList.add('qr--current');
else item.addEventListener('click', ()=>{
else item.addEventListener('click', () => {
this.editorPopup.completeAffirmative();
qr.showEditor();
});
@@ -588,7 +582,7 @@ export class QuickReply {
});
};
const updateScrollDebounced = updateScroll;
const updateSyntaxEnabled = ()=>{
const updateSyntaxEnabled = () => {
if (syntax.checked) {
dom.querySelector('#qr--modal-messageHolder').classList.remove('qr--noSyntax');
} else {
@@ -623,7 +617,7 @@ export class QuickReply {
// @ts-ignore
if (navigator.keyboard) {
// @ts-ignore
navigator.keyboard.getLayoutMap().then(it=>dom.querySelector('#qr--modal-commentKey').textContent = it.get('Backslash'));
navigator.keyboard.getLayoutMap().then(it => dom.querySelector('#qr--modal-commentKey').textContent = it.get('Backslash'));
} else {
dom.querySelector('#qr--modal-commentKey').closest('small').remove();
}
@@ -632,12 +626,12 @@ export class QuickReply {
const message = dom.querySelector('#qr--modal-message');
this.editorMessage = message;
message.value = this.message;
const updateMessageDebounced = debounce((value)=>this.updateMessage(value), 10);
const updateMessageDebounced = debounce((value) => this.updateMessage(value), 10);
message.addEventListener('input', () => {
updateMessageDebounced(message.value);
updateScrollDebounced();
}, { passive:true });
const getLineStart = ()=>{
}, { passive: true });
const getLineStart = () => {
const start = message.selectionStart;
let lineStart;
if (start == 0 || message.value[start - 1] == '\n') {
@@ -651,7 +645,7 @@ export class QuickReply {
}
return lineStart;
};
message.addEventListener('keydown', async(evt) => {
message.addEventListener('keydown', async (evt) => {
if (this.isExecuting) return;
if (evt.key == 'Tab' && !evt.shiftKey && !evt.ctrlKey && !evt.altKey) {
// increase indent
@@ -668,13 +662,13 @@ export class QuickReply {
document.execCommand('insertText', false, `\t${affectedLines.join('\n\t')}`);
message.selectionStart = start + 1;
message.selectionEnd = end + affectedLines.length;
message.dispatchEvent(new Event('input', { bubbles:true }));
message.dispatchEvent(new Event('input', { bubbles: true }));
} else if (!(ac.isReplaceable && ac.isActive)) {
evt.stopImmediatePropagation();
evt.stopPropagation();
// document.execCommand is deprecated (and potentially buggy in some browsers) but the only way to retain undo-history
document.execCommand('insertText', false, '\t');
message.dispatchEvent(new Event('input', { bubbles:true }));
message.dispatchEvent(new Event('input', { bubbles: true }));
}
} else if (evt.key == 'Tab' && evt.shiftKey && !evt.ctrlKey && !evt.altKey) {
// decrease indent
@@ -686,7 +680,7 @@ export class QuickReply {
const lineStart = getLineStart();
message.selectionStart = lineStart;
const affectedLines = message.value.substring(lineStart, end).split('\n');
const newText = affectedLines.map(it=>it.replace(/^\t/, '')).join('\n');
const newText = affectedLines.map(it => it.replace(/^\t/, '')).join('\n');
const delta = affectedLines.join('\n').length - newText.length;
// document.execCommand is deprecated (and potentially buggy in some browsers) but the only way to retain undo-history
if (delta > 0) {
@@ -697,7 +691,7 @@ export class QuickReply {
}
message.selectionStart = start - (affectedLines[0].startsWith('\t') ? 1 : 0);
message.selectionEnd = end - delta;
message.dispatchEvent(new Event('input', { bubbles:true }));
message.dispatchEvent(new Event('input', { bubbles: true }));
} else {
message.selectionStart = start;
}
@@ -714,7 +708,7 @@ export class QuickReply {
document.execCommand('insertText', false, `\n${indent}`);
message.selectionStart = start + 1 + indent.length;
message.selectionEnd = message.selectionStart;
message.dispatchEvent(new Event('input', { bubbles:true }));
message.dispatchEvent(new Event('input', { bubbles: true }));
}
} else if (evt.key == 'Enter' && evt.ctrlKey && !evt.shiftKey && !evt.altKey) {
if (executeShortcut.checked) {
@@ -751,7 +745,7 @@ export class QuickReply {
parser.parse(message.value, false);
const start = message.selectionStart;
const end = message.selectionEnd;
const comment = parser.commandIndex.findLast(it=>it.name == '*' && (it.start <= start && it.end >= start || it.start <= end && it.end >= end));
const comment = parser.commandIndex.findLast(it => it.name == '*' && (it.start <= start && it.end >= start || it.start <= end && it.end >= end));
if (comment) {
// uncomment
let content = message.value.slice(comment.start + 1, comment.end - 1);
@@ -778,15 +772,15 @@ export class QuickReply {
message.selectionStart = start + 3;
message.selectionEnd = end + 3;
}
message.dispatchEvent(new Event('input', { bubbles:true }));
message.dispatchEvent(new Event('input', { bubbles: true }));
}
});
const ac = await setSlashCommandAutoComplete(message, true);
message.addEventListener('wheel', (evt)=>{
message.addEventListener('wheel', (evt) => {
updateScrollDebounced(evt);
});
// @ts-ignore
message.addEventListener('scroll', (evt)=>{
message.addEventListener('scroll', (evt) => {
updateScrollDebounced();
});
let preBreakPointStart;
@@ -794,7 +788,7 @@ export class QuickReply {
/**
* @param {SlashCommandBreakPoint} bp
*/
const removeBreakpoint = (bp)=>{
const removeBreakpoint = (bp) => {
// start at -1 because "/" is not included in start-end
let start = bp.start - 1;
// step left until forward slash "/"
@@ -812,7 +806,7 @@ export class QuickReply {
message.selectionEnd = end;
// document.execCommand is deprecated (and potentially buggy in some browsers) but the only way to retain undo-history
document.execCommand('insertText', false, '');
message.dispatchEvent(new Event('input', { bubbles:true }));
message.dispatchEvent(new Event('input', { bubbles: true }));
let postStart = preBreakPointStart;
let postEnd = preBreakPointEnd;
// set caret back to where it was
@@ -834,12 +828,12 @@ export class QuickReply {
// selection end was behind breakpoint: move back by length of removed string
postEnd = preBreakPointEnd - (end - start);
}
return { start:postStart, end:postEnd };
return { start: postStart, end: postEnd };
};
/**
* @param {SlashCommandExecutor} cmd
*/
const addBreakpoint = (cmd)=>{
const addBreakpoint = (cmd) => {
// start at -1 because "/" is not included in start-end
let start = cmd.start - 1;
let indent = '';
@@ -860,16 +854,16 @@ export class QuickReply {
message.selectionEnd = start;
// document.execCommand is deprecated (and potentially buggy in some browsers) but the only way to retain undo-history
document.execCommand('insertText', false, breakpointText);
message.dispatchEvent(new Event('input', { bubbles:true }));
message.dispatchEvent(new Event('input', { bubbles: true }));
return breakpointText.length;
};
const toggleBreakpoint = ()=>{
const toggleBreakpoint = () => {
const idx = message.selectionStart;
let postStart = preBreakPointStart;
let postEnd = preBreakPointEnd;
const parser = new SlashCommandParser();
parser.parse(message.value, false);
const cmdIdx = parser.commandIndex.findLastIndex(it=>it.start <= idx);
const cmdIdx = parser.commandIndex.findLastIndex(it => it.start <= idx);
if (cmdIdx > -1) {
const cmd = parser.commandIndex[cmdIdx];
if (cmd instanceof SlashCommandBreakPoint) {
@@ -891,12 +885,12 @@ export class QuickReply {
message.selectionEnd = postEnd;
}
};
message.addEventListener('pointerdown', (evt)=>{
message.addEventListener('pointerdown', (evt) => {
if (!evt.ctrlKey || !evt.altKey) return;
preBreakPointStart = message.selectionStart;
preBreakPointEnd = message.selectionEnd;
});
message.addEventListener('pointerup', async(evt)=>{
message.addEventListener('pointerup', async (evt) => {
if (!evt.ctrlKey || !evt.altKey || message.selectionStart != message.selectionEnd) return;
toggleBreakpoint();
});
@@ -910,11 +904,11 @@ export class QuickReply {
});
window.addEventListener('resize', resizeListener);
updateSyntaxEnabled();
const updateSyntax = ()=>{
const updateSyntax = () => {
if (messageSyntaxInner && syntax.checked) {
morphdom(
messageSyntaxInner,
`<div>${hljs.highlight(`${message.value}${message.value.slice(-1) == '\n' ? ' ' : ''}`, { language:'stscript', ignoreIllegals:true })?.value}</div>`,
`<div>${hljs.highlight(`${message.value}${message.value.slice(-1) == '\n' ? ' ' : ''}`, { language: 'stscript', ignoreIllegals: true })?.value}</div>`,
{ childrenOnly: true },
);
updateScrollDebounced();
@@ -924,7 +918,7 @@ export class QuickReply {
const fpsTime = 1000 / 30;
let lastMessageValue = null;
let wasSyntax = null;
const updateSyntaxLoop = ()=>{
const updateSyntaxLoop = () => {
const now = Date.now();
// fps limit
if (now - lastSyntaxUpdate < fpsTime) return requestAnimationFrame(updateSyntaxLoop);
@@ -945,7 +939,7 @@ export class QuickReply {
updateSyntax();
requestAnimationFrame(updateSyntaxLoop);
};
requestAnimationFrame(()=>updateSyntaxLoop());
requestAnimationFrame(() => updateSyntaxLoop());
message.style.setProperty('text-shadow', 'none', 'important');
updateWrap();
updateTabSize();
@@ -955,7 +949,7 @@ export class QuickReply {
const tpl = dom.querySelector('#qr--ctxItem');
const linkList = dom.querySelector('#qr--ctxEditor');
const fillQrSetSelect = (/**@type {HTMLSelectElement}*/select, /**@type {QuickReplyContextLink}*/ link) => {
[{ name: 'Select a QR set' }, ...QuickReplySet.list.toSorted((a,b)=>a.name.toLowerCase().localeCompare(b.name.toLowerCase()))].forEach(qrs => {
[{ name: 'Select a QR set' }, ...QuickReplySet.list.toSorted((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()))].forEach(qrs => {
const opt = document.createElement('option'); {
opt.value = qrs.name;
opt.textContent = qrs.name;
@@ -1002,7 +996,7 @@ export class QuickReply {
addCtxItem(link, this.contextList.length - 1);
});
const onContextSort = () => {
this.contextList = Array.from(linkList.querySelectorAll('.qr--ctxItem')).map((it,idx) => {
this.contextList = Array.from(linkList.querySelectorAll('.qr--ctxItem')).map((it, idx) => {
const link = this.contextList[Number(it.getAttribute('data-order'))];
it.setAttribute('data-order', String(idx));
return link;
@@ -1019,63 +1013,63 @@ export class QuickReply {
/**@type {HTMLInputElement}*/
const preventAutoExecute = dom.querySelector('#qr--preventAutoExecute');
preventAutoExecute.checked = this.preventAutoExecute;
preventAutoExecute.addEventListener('click', ()=>{
preventAutoExecute.addEventListener('click', () => {
this.preventAutoExecute = preventAutoExecute.checked;
this.updateContext();
});
/**@type {HTMLInputElement}*/
const isHidden = dom.querySelector('#qr--isHidden');
isHidden.checked = this.isHidden;
isHidden.addEventListener('click', ()=>{
isHidden.addEventListener('click', () => {
this.isHidden = isHidden.checked;
this.updateContext();
});
/**@type {HTMLInputElement}*/
const executeOnStartup = dom.querySelector('#qr--executeOnStartup');
executeOnStartup.checked = this.executeOnStartup;
executeOnStartup.addEventListener('click', ()=>{
executeOnStartup.addEventListener('click', () => {
this.executeOnStartup = executeOnStartup.checked;
this.updateContext();
});
/**@type {HTMLInputElement}*/
const executeOnUser = dom.querySelector('#qr--executeOnUser');
executeOnUser.checked = this.executeOnUser;
executeOnUser.addEventListener('click', ()=>{
executeOnUser.addEventListener('click', () => {
this.executeOnUser = executeOnUser.checked;
this.updateContext();
});
/**@type {HTMLInputElement}*/
const executeOnAi = dom.querySelector('#qr--executeOnAi');
executeOnAi.checked = this.executeOnAi;
executeOnAi.addEventListener('click', ()=>{
executeOnAi.addEventListener('click', () => {
this.executeOnAi = executeOnAi.checked;
this.updateContext();
});
/**@type {HTMLInputElement}*/
const executeOnChatChange = dom.querySelector('#qr--executeOnChatChange');
executeOnChatChange.checked = this.executeOnChatChange;
executeOnChatChange.addEventListener('click', ()=>{
executeOnChatChange.addEventListener('click', () => {
this.executeOnChatChange = executeOnChatChange.checked;
this.updateContext();
});
/**@type {HTMLInputElement}*/
const executeOnGroupMemberDraft = dom.querySelector('#qr--executeOnGroupMemberDraft');
executeOnGroupMemberDraft.checked = this.executeOnGroupMemberDraft;
executeOnGroupMemberDraft.addEventListener('click', ()=>{
executeOnGroupMemberDraft.addEventListener('click', () => {
this.executeOnGroupMemberDraft = executeOnGroupMemberDraft.checked;
this.updateContext();
});
/**@type {HTMLInputElement}*/
const executeBeforeGeneration = dom.querySelector('#qr--executeBeforeGeneration');
executeBeforeGeneration.checked = this.executeBeforeGeneration;
executeBeforeGeneration.addEventListener('click', ()=>{
executeBeforeGeneration.addEventListener('click', () => {
this.executeBeforeGeneration = executeBeforeGeneration.checked;
this.updateContext();
});
/**@type {HTMLInputElement}*/
const executeOnNewChat = dom.querySelector('#qr--executeOnNewChat');
executeOnNewChat.checked = this.executeOnNewChat;
executeOnNewChat.addEventListener('click', ()=>{
executeOnNewChat.addEventListener('click', () => {
this.executeOnNewChat = executeOnNewChat.checked;
this.updateContext();
});
@@ -1102,13 +1096,13 @@ export class QuickReply {
/**@type {HTMLElement}*/
const executeBtn = dom.querySelector('#qr--modal-execute');
this.editorExecuteBtn = executeBtn;
executeBtn.addEventListener('click', async()=>{
executeBtn.addEventListener('click', async () => {
await this.executeFromEditor();
});
/**@type {HTMLElement}*/
const executeBtnPause = dom.querySelector('#qr--modal-pause');
this.editorExecuteBtnPause = executeBtnPause;
executeBtnPause.addEventListener('click', async()=>{
executeBtnPause.addEventListener('click', async () => {
if (this.abortController) {
if (this.abortController.signal.paused) {
this.abortController.continue('Continue button clicked');
@@ -1122,7 +1116,7 @@ export class QuickReply {
/**@type {HTMLElement}*/
const executeBtnStop = dom.querySelector('#qr--modal-stop');
this.editorExecuteBtnStop = executeBtnStop;
executeBtnStop.addEventListener('click', async()=>{
executeBtnStop.addEventListener('click', async () => {
this.abortController?.abort('Stop button clicked');
});
@@ -1131,49 +1125,49 @@ export class QuickReply {
const inputMirror = dom.querySelector('#qr--modal-send_textarea');
// @ts-ignore
inputMirror.value = inputOg.value;
const inputOgMo = new MutationObserver(muts=>{
if (muts.find(it=>[...it.removedNodes].includes(inputMirror) || [...it.removedNodes].find(n=>n.contains(inputMirror)))) {
const inputOgMo = new MutationObserver(muts => {
if (muts.find(it => [...it.removedNodes].includes(inputMirror) || [...it.removedNodes].find(n => n.contains(inputMirror)))) {
inputOg.removeEventListener('input', inputOgListener);
}
});
inputOgMo.observe(document.body, { childList:true });
const inputOgListener = ()=>{
inputOgMo.observe(document.body, { childList: true });
const inputOgListener = () => {
// @ts-ignore
inputMirror.value = inputOg.value;
};
inputOg.addEventListener('input', inputOgListener);
inputMirror.addEventListener('input', ()=>{
inputMirror.addEventListener('input', () => {
// @ts-ignore
inputOg.value = inputMirror.value;
});
/**@type {HTMLElement}*/
const resumeBtn = dom.querySelector('#qr--modal-resume');
resumeBtn.addEventListener('click', ()=>{
resumeBtn.addEventListener('click', () => {
this.debugController?.resume();
});
/**@type {HTMLElement}*/
const stepBtn = dom.querySelector('#qr--modal-step');
stepBtn.addEventListener('click', ()=>{
stepBtn.addEventListener('click', () => {
this.debugController?.step();
});
/**@type {HTMLElement}*/
const stepIntoBtn = dom.querySelector('#qr--modal-stepInto');
stepIntoBtn.addEventListener('click', ()=>{
stepIntoBtn.addEventListener('click', () => {
this.debugController?.stepInto();
});
/**@type {HTMLElement}*/
const stepOutBtn = dom.querySelector('#qr--modal-stepOut');
stepOutBtn.addEventListener('click', ()=>{
stepOutBtn.addEventListener('click', () => {
this.debugController?.stepOut();
});
/**@type {HTMLElement}*/
const minimizeBtn = dom.querySelector('#qr--modal-minimize');
minimizeBtn.addEventListener('click', ()=>{
minimizeBtn.addEventListener('click', () => {
this.editorDom.classList.add('qr--minimized');
});
const maximizeBtn = dom.querySelector('#qr--modal-maximize');
maximizeBtn.addEventListener('click', ()=>{
maximizeBtn.addEventListener('click', () => {
this.editorDom.classList.remove('qr--minimized');
});
/**@type {boolean}*/
@@ -1182,23 +1176,23 @@ export class QuickReply {
let wStart;
/**@type {HTMLElement}*/
const resizeHandle = dom.querySelector('#qr--resizeHandle');
resizeHandle.addEventListener('pointerdown', (evt)=>{
resizeHandle.addEventListener('pointerdown', (evt) => {
if (isResizing) return;
isResizing = true;
evt.preventDefault();
resizeStart = evt.x;
// @ts-ignore
wStart = dom.querySelector('#qr--qrOptions').offsetWidth;
const dragListener = debounce((evt)=>{
const dragListener = debounce((evt) => {
const w = wStart + resizeStart - evt.x;
// @ts-ignore
dom.querySelector('#qr--qrOptions').style.setProperty('--width', `${w}px`);
}, 5);
window.addEventListener('pointerup', ()=>{
window.addEventListener('pointerup', () => {
// @ts-ignore
window.removeEventListener('pointermove', dragListener);
isResizing = false;
}, { once:true });
}, { once: true });
// @ts-ignore
window.addEventListener('pointermove', dragListener);
});
@@ -1221,13 +1215,13 @@ export class QuickReply {
}
this.clone.style.position = 'fixed';
this.clone.style.visibility = 'hidden';
const mo = new MutationObserver(muts=>{
if (muts.find(it=>[...it.removedNodes].includes(this.editorMessage) || [...it.removedNodes].find(n=>n.contains(this.editorMessage)))) {
const mo = new MutationObserver(muts => {
if (muts.find(it => [...it.removedNodes].includes(this.editorMessage) || [...it.removedNodes].find(n => n.contains(this.editorMessage)))) {
this.clone?.remove();
this.clone = null;
}
});
mo.observe(document.body, { childList:true });
mo.observe(document.body, { childList: true });
}
document.body.append(this.clone);
this.clone.style.width = `${inputRect.width}px`;
@@ -1258,7 +1252,7 @@ export class QuickReply {
}
async executeFromEditor() {
if (this.isExecuting) return;
this.editorPopup.onClosing = ()=>false;
this.editorPopup.onClosing = () => false;
const uuidCheck = /^[0-9a-z]{8}(-[0-9a-z]{4}){3}-[0-9a-z]{12}$/;
const oText = this.message;
this.isExecuting = true;
@@ -1298,19 +1292,19 @@ export class QuickReply {
});
};
const updateScrollDebounced = updateScroll;
syntax.addEventListener('wheel', (evt)=>{
syntax.addEventListener('wheel', (evt) => {
updateScrollDebounced(evt);
});
// @ts-ignore
syntax.addEventListener('scroll', (evt)=>{
syntax.addEventListener('scroll', (evt) => {
updateScrollDebounced();
});
try {
this.abortController = new SlashCommandAbortController();
this.debugController = new SlashCommandDebugController();
this.debugController.onBreakPoint = async(closure, executor)=>{
this.debugController.onBreakPoint = async (closure, executor) => {
this.editorDom.classList.add('qr--isPaused');
syntax.innerHTML = hljs.highlight(`${closure.fullText}${closure.fullText.slice(-1) == '\n' ? ' ' : ''}`, { language:'stscript', ignoreIllegals:true })?.value;
syntax.innerHTML = hljs.highlight(`${closure.fullText}${closure.fullText.slice(-1) == '\n' ? ' ' : ''}`, { language: 'stscript', ignoreIllegals: true })?.value;
this.editorMessageLabel.innerHTML = '';
if (uuidCheck.test(closure.source)) {
const p0 = document.createElement('span'); {
@@ -1318,7 +1312,7 @@ export class QuickReply {
this.editorMessageLabel.append(p0);
}
const p1 = document.createElement('strong'); {
p1.textContent = executor.source.slice(0,5);
p1.textContent = executor.source.slice(0, 5);
this.editorMessageLabel.append(p1);
}
const p2 = document.createElement('span'); {
@@ -1340,7 +1334,7 @@ export class QuickReply {
/**
* @param {SlashCommandScope} scope
*/
const buildVars = (scope, isCurrent = false)=>{
const buildVars = (scope, isCurrent = false) => {
if (!isCurrent) {
ci--;
}
@@ -1358,7 +1352,7 @@ export class QuickReply {
}
wrap.append(namedTitle);
}
const keys = new Set([...Object.keys(this.debugController.namedArguments ?? {}), ...(executor.namedArgumentList ?? []).map(it=>it.name)]);
const keys = new Set([...Object.keys(this.debugController.namedArguments ?? {}), ...(executor.namedArgumentList ?? []).map(it => it.name)]);
for (const key of keys) {
if (key[0] == '_') continue;
const item = document.createElement('div'); {
@@ -1371,7 +1365,7 @@ export class QuickReply {
const vUnresolved = document.createElement('div'); {
vUnresolved.classList.add('qr--val');
vUnresolved.classList.add('qr--singleCol');
const val = executor.namedArgumentList.find(it=>it.name == key)?.value;
const val = executor.namedArgumentList.find(it => it.name == key)?.value;
if (val instanceof SlashCommandClosure) {
vUnresolved.classList.add('qr--closure');
vUnresolved.title = val.rawText;
@@ -1437,7 +1431,7 @@ export class QuickReply {
// @ts-ignore
while (unnamed.length < executor.unnamedArgumentList?.length ?? 0) unnamed.push(undefined);
// @ts-ignore
unnamed = unnamed.map((it,idx)=>[executor.unnamedArgumentList?.[idx], it]);
unnamed = unnamed.map((it, idx) => [executor.unnamedArgumentList?.[idx], it]);
// @ts-ignore
for (const arg of unnamed) {
i++;
@@ -1511,7 +1505,7 @@ export class QuickReply {
title.textContent = isCurrent ? 'Current Scope' : 'Parent Scope';
if (c.source == source) {
let hi;
title.addEventListener('pointerenter', ()=>{
title.addEventListener('pointerenter', () => {
const loc = this.getEditorPosition(Math.max(0, c.executorList[0].start - 1), c.executorList.slice(-1)[0].end, c.fullText);
const layer = syntax.getBoundingClientRect();
hi = document.createElement('div');
@@ -1522,7 +1516,7 @@ export class QuickReply {
hi.style.height = `${loc.bottom - loc.top}px`;
syntax.append(hi);
});
title.addEventListener('pointerleave', ()=>hi?.remove());
title.addEventListener('pointerleave', () => hi?.remove());
}
wrap.append(title);
}
@@ -1635,7 +1629,7 @@ export class QuickReply {
}
return wrap;
};
const buildStack = ()=>{
const buildStack = () => {
const wrap = document.createElement('div'); {
wrap.classList.add('qr--stack');
const title = document.createElement('div'); {
@@ -1651,7 +1645,7 @@ export class QuickReply {
item.classList.add('qr--item');
if (executor.source == source) {
let hi;
item.addEventListener('pointerenter', ()=>{
item.addEventListener('pointerenter', () => {
const loc = this.getEditorPosition(Math.max(0, executor.start - 1), executor.end, c.fullText);
const layer = syntax.getBoundingClientRect();
hi = document.createElement('div');
@@ -1662,7 +1656,7 @@ export class QuickReply {
hi.style.height = `${loc.bottom - loc.top}px`;
syntax.append(hi);
});
item.addEventListener('pointerleave', ()=>hi?.remove());
item.addEventListener('pointerleave', () => hi?.remove());
}
const cmd = document.createElement('div'); {
cmd.classList.add('qr--cmd');
@@ -1678,7 +1672,7 @@ export class QuickReply {
if (uuidCheck.test(executor.source)) {
const p1 = document.createElement('span'); {
p1.classList.add('qr--fixed');
p1.textContent = executor.source.slice(0,5);
p1.textContent = executor.source.slice(0, 5);
src.append(p1);
}
const p2 = document.createElement('span'); {
@@ -1756,7 +1750,7 @@ export class QuickReply {
this.editorMessageLabel.innerHTML = '';
this.editorMessageLabel.textContent = 'Message / Command: ';
this.editorMessage.value = oText;
this.editorMessage.dispatchEvent(new Event('input', { bubbles:true }));
this.editorMessage.dispatchEvent(new Event('input', { bubbles: true }));
this.editorExecutePromise = null;
this.editorExecuteBtn.classList.remove('qr--busy');
this.editorDom.classList.remove('qr--isExecuting');
@@ -1769,8 +1763,6 @@ export class QuickReply {
}
delete() {
if (this.onDelete) {
this.unrender();
@@ -1870,7 +1862,7 @@ export class QuickReply {
this.updateContext();
}
removeContextLink(setName) {
const idx = this.contextList.findIndex(it=>it.set.name == setName);
const idx = this.contextList.findIndex(it => it.set.name == setName);
if (idx > -1) {
this.contextList.splice(idx, 1);
this.updateContext();
@@ -1908,8 +1900,6 @@ export class QuickReply {
}
toJSON() {
return {
id: this.id,
@@ -13,25 +13,21 @@ export class QuickReplyConfig {
/**@type {HTMLElement}*/ setListDom;
static from(props) {
props.setList = props.setList?.map(it=>QuickReplySetLink.from(it))?.filter(it=>it.set) ?? [];
props.setList = props.setList?.map(it => QuickReplySetLink.from(it))?.filter(it => it.set) ?? [];
const instance = Object.assign(new this(), props);
instance.init();
return instance;
}
init() {
this.setList.forEach(it=>this.hookQuickReplyLink(it));
this.setList.forEach(it => this.hookQuickReplyLink(it));
}
hasSet(qrs) {
return this.setList.find(it=>it.set == qrs) != null;
return this.setList.find(it => it.set == qrs) != null;
}
addSet(qrs, isVisible = true) {
if (!this.hasSet(qrs)) {
@@ -45,7 +41,7 @@ export class QuickReplyConfig {
}
}
removeSet(qrs) {
const idx = this.setList.findIndex(it=>it.set == qrs);
const idx = this.setList.findIndex(it => it.set == qrs);
if (idx > -1) {
this.setList.splice(idx, 1);
this.update();
@@ -54,13 +50,11 @@ export class QuickReplyConfig {
}
renderSettingsInto(/**@type {HTMLElement}*/root) {
/**@type {HTMLElement}*/
this.setListDom = root.querySelector('.qr--setList');
root.querySelector('.qr--setListAdd').addEventListener('click', ()=>{
const newSet = QuickReplySet.list.find(qr=>!this.setList.find(qrl=>qrl.set == qr));
root.querySelector('.qr--setListAdd').addEventListener('click', () => {
const newSet = QuickReplySet.list.find(qr => !this.setList.find(qrl => qrl.set == qr));
if (newSet) {
this.addSet(newSet);
} else {
@@ -74,14 +68,14 @@ export class QuickReplyConfig {
// @ts-ignore
$(this.setListDom).sortable({
delay: getSortableDelay(),
stop: ()=>this.onSetListSort(),
stop: () => this.onSetListSort(),
});
this.setList.filter(it=>!it.set.isDeleted).forEach((qrl,idx)=>this.setListDom.append(qrl.renderSettings(idx)));
this.setList.filter(it => !it.set.isDeleted).forEach((qrl, idx) => this.setListDom.append(qrl.renderSettings(idx)));
}
onSetListSort() {
this.setList = Array.from(this.setListDom.children).map((it,idx)=>{
this.setList = Array.from(this.setListDom.children).map((it, idx) => {
const qrl = this.setList[Number(it.getAttribute('data-order'))];
qrl.index = idx;
it.setAttribute('data-order', String(idx));
@@ -91,15 +85,13 @@ export class QuickReplyConfig {
}
/**
* @param {QuickReplySetLink} qrl
*/
hookQuickReplyLink(qrl) {
qrl.onDelete = ()=>this.deleteQuickReplyLink(qrl);
qrl.onUpdate = ()=>this.update();
qrl.onRequestEditSet = ()=>this.requestEditSet(qrl.set);
qrl.onDelete = () => this.deleteQuickReplyLink(qrl);
qrl.onUpdate = () => this.update();
qrl.onRequestEditSet = () => this.requestEditSet(qrl.set);
}
deleteQuickReplyLink(qrl) {
@@ -8,8 +8,6 @@ export class QuickReplyContextLink {
}
/**@type {QuickReplySet}*/ set;
/**@type {Boolean}*/ isChained = false;
@@ -24,7 +24,7 @@ export class QuickReplySet {
* @param {string} name - name of the QuickReplySet
*/
static get(name) {
return this.list.find(it=>it.name == name);
return this.list.find(it => it.name == name);
}
/**@type {string}*/ name;
@@ -42,11 +42,11 @@ export class QuickReplySet {
/**@type {HTMLElement}*/ settingsDom;
constructor() {
this.save = debounceAsync(()=>this.performSave(), 200);
this.save = debounceAsync(() => this.performSave(), 200);
}
init() {
this.qrList.forEach(qr=>this.hookQuickReply(qr));
this.qrList.forEach(qr => this.hookQuickReply(qr));
}
unrender() {
@@ -60,7 +60,7 @@ export class QuickReplySet {
this.dom = root;
root.classList.add('qr--buttons');
this.updateColor();
this.qrList.filter(qr=>!qr.isHidden).forEach(qr=>{
this.qrList.filter(qr => !qr.isHidden).forEach(qr => {
root.append(qr.render());
});
}
@@ -70,7 +70,7 @@ export class QuickReplySet {
rerender() {
if (!this.dom) return;
this.dom.innerHTML = '';
this.qrList.filter(qr=>!qr.isHidden).forEach(qr=>{
this.qrList.filter(qr => !qr.isHidden).forEach(qr => {
this.dom.append(qr.render());
});
}
@@ -95,7 +95,7 @@ export class QuickReplySet {
if (!this.settingsDom) {
this.settingsDom = document.createElement('div'); {
this.settingsDom.classList.add('qr--set-qrListContents');
this.qrList.forEach((qr,idx)=>{
this.qrList.forEach((qr, idx) => {
this.renderSettingsItem(qr, idx);
});
}
@@ -138,12 +138,12 @@ export class QuickReplySet {
*/
async executeWithOptions(qr, options = {}) {
options = Object.assign({
message:null,
isAutoExecute:false,
isEditor:false,
isRun:false,
scope:null,
executionOptions:{},
message: null,
isAutoExecute: false,
isEditor: false,
isRun: false,
scope: null,
executionOptions: {},
}, options);
const execOptions = options.executionOptions;
/**@type {HTMLTextAreaElement}*/
@@ -208,9 +208,8 @@ export class QuickReplySet {
}
addQuickReply(data = {}) {
const id = Math.max(this.idIndex, this.qrList.reduce((max,qr)=>Math.max(max,qr.id),0)) + 1;
data.id =
this.idIndex = id + 1;
const id = Math.max(this.idIndex, this.qrList.reduce((max, qr) => Math.max(max, qr.id), 0)) + 1;
data.id = this.idIndex = id + 1;
const qr = QuickReply.from(data);
this.qrList.push(qr);
this.hookQuickReply(qr);
@@ -257,11 +256,11 @@ export class QuickReplySet {
*/
hookQuickReply(qr) {
// @ts-ignore
qr.onDebug = ()=>this.debug(qr);
qr.onExecute = (_, options)=>this.executeWithOptions(qr, options);
qr.onDelete = ()=>this.removeQuickReply(qr);
qr.onUpdate = ()=>this.save();
qr.onInsertBefore = (qrJson)=>{
qr.onDebug = () => this.debug(qr);
qr.onExecute = (_, options) => this.executeWithOptions(qr, options);
qr.onDelete = () => this.removeQuickReply(qr);
qr.onUpdate = () => this.save();
qr.onInsertBefore = (qrJson) => {
this.addQuickReplyFromText(qrJson);
const newQr = this.qrList.pop();
this.qrList.splice(this.qrList.indexOf(qr), 0, newQr);
@@ -270,7 +269,7 @@ export class QuickReplySet {
}
this.save();
};
qr.onTransfer = async()=>{
qr.onTransfer = async () => {
/**@type {HTMLSelectElement} */
let sel;
let isCopy = false;
@@ -301,14 +300,14 @@ export class QuickReplySet {
sel.append(opt);
}
}
sel.addEventListener('keyup', (evt)=>{
sel.addEventListener('keyup', (evt) => {
if (evt.key == 'Shift') {
// @ts-ignore
(dlg.dom ?? dlg.dlg).classList.remove('qr--isCopy');
return;
}
});
sel.addEventListener('keydown', (evt)=>{
sel.addEventListener('keydown', (evt) => {
if (evt.key == 'Shift') {
// @ts-ignore
(dlg.dom ?? dlg.dlg).classList.add('qr--isCopy');
@@ -330,12 +329,12 @@ export class QuickReplySet {
dom.append(hintP);
}
}
const dlg = new Popup(dom, POPUP_TYPE.CONFIRM, null, { okButton:'Transfer', cancelButton:'Cancel' });
const dlg = new Popup(dom, POPUP_TYPE.CONFIRM, null, { okButton: 'Transfer', cancelButton: 'Cancel' });
const copyBtn = document.createElement('div'); {
copyBtn.classList.add('qr--copy');
copyBtn.classList.add('menu_button');
copyBtn.textContent = 'Copy';
copyBtn.addEventListener('click', ()=>{
copyBtn.addEventListener('click', () => {
isCopy = true;
dlg.completeAffirmative();
});
@@ -346,7 +345,7 @@ export class QuickReplySet {
sel.focus();
await prom;
if (dlg.result == POPUP_RESULT.AFFIRMATIVE) {
const qrs = QuickReplySet.list.find(it=>it.name == sel.value);
const qrs = QuickReplySet.list.find(it => it.name == sel.value);
qrs.addQuickReply(qr.toJSON());
if (!isCopy) {
qr.delete();
@@ -9,8 +9,6 @@ export class QuickReplySetLink {
}
/**@type {QuickReplySet}*/ set;
/**@type {Boolean}*/ isVisible = true;
@@ -23,8 +21,6 @@ export class QuickReplySetLink {
/**@type {HTMLElement}*/ settingsDom;
renderSettings(idx) {
this.index = idx;
const item = document.createElement('div'); {
@@ -40,12 +36,12 @@ export class QuickReplySetLink {
const set = document.createElement('select'); {
set.classList.add('qr--set');
// fix for jQuery sortable breaking childrens' touch events
set.addEventListener('touchstart', (evt)=>evt.stopPropagation());
set.addEventListener('change', ()=>{
set.addEventListener('touchstart', (evt) => evt.stopPropagation());
set.addEventListener('change', () => {
this.set = QuickReplySet.get(set.value);
this.update();
});
QuickReplySet.list.toSorted((a,b)=>a.name.toLowerCase().localeCompare(b.name.toLowerCase())).forEach(qrs=>{
QuickReplySet.list.toSorted((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase())).forEach(qrs => {
const opt = document.createElement('option'); {
opt.value = qrs.name;
opt.textContent = qrs.name;
@@ -61,7 +57,7 @@ export class QuickReplySetLink {
const cb = document.createElement('input'); {
cb.type = 'checkbox';
cb.checked = this.isVisible;
cb.addEventListener('click', ()=>{
cb.addEventListener('click', () => {
this.isVisible = cb.checked;
this.update();
});
@@ -76,7 +72,7 @@ export class QuickReplySetLink {
edit.classList.add('fa-solid');
edit.classList.add('fa-pencil');
edit.title = 'Edit quick reply set';
edit.addEventListener('click', ()=>this.requestEditSet());
edit.addEventListener('click', () => this.requestEditSet());
item.append(edit);
}
const del = document.createElement('div'); {
@@ -86,7 +82,7 @@ export class QuickReplySetLink {
del.classList.add('fa-solid');
del.classList.add('fa-trash-can');
del.title = 'Remove quick reply set';
del.addEventListener('click', ()=>this.delete());
del.addEventListener('click', () => this.delete());
item.append(del);
}
}
@@ -98,8 +94,6 @@ export class QuickReplySetLink {
}
update() {
if (this.onUpdate) {
this.onUpdate(this);
@@ -118,8 +112,6 @@ export class QuickReplySetLink {
}
toJSON() {
return {
set: this.set.name,
@@ -15,8 +15,6 @@ export class QuickReplySettings {
}
/**@type {Boolean}*/ isEnabled = false;
/**@type {Boolean}*/ isCombined = false;
/**@type {Boolean}*/ isPopout = false;
@@ -50,8 +48,6 @@ export class QuickReplySettings {
/**@type {Function}*/ onRequestEditSet;
init() {
this.hookConfig(this.config);
this.hookConfig(this.chatConfig);
@@ -60,8 +56,8 @@ export class QuickReplySettings {
hookConfig(config) {
if (config) {
config.onUpdate = ()=>this.save();
config.onRequestEditSet = (qrs)=>this.requestEditSet(qrs);
config.onUpdate = () => this.save();
config.onRequestEditSet = (qrs) => this.requestEditSet(qrs);
}
}
unhookConfig(config) {
@@ -72,8 +68,6 @@ export class QuickReplySettings {
}
save() {
extension_settings.quickReplyV2 = this.toJSON();
saveSettingsDebounced();
@@ -16,15 +16,11 @@ export class SlashCommandHandler {
/** @type {QuickReplyApi} */ api;
constructor(/** @type {QuickReplyApi} */api) {
this.api = api;
}
init() {
function getExecutionIcons(/** @type {QuickReply} */ qr) {
let icons = '';
@@ -56,7 +52,7 @@ export class SlashCommandHandler {
qrIds: (executor) => QuickReplySet.get(String(executor.namedArgumentList.find(x => x.name == 'set')?.value))?.qrList.map(qr => {
const icons = getExecutionIcons(qr);
const message = `${qr.automationId ? `[${qr.automationId}]` : ''}${icons ? `[auto: ${icons}]` : ''} ${qr.title || qr.message}`.trim();
return new SlashCommandEnumValue(qr.label, message, enumTypes.enum, enumIcons.qr, null, ()=>qr.id.toString(), true);
return new SlashCommandEnumValue(qr.label, message, enumTypes.enum, enumIcons.qr, null, () => qr.id.toString(), true);
}) ?? [],
/** All QRs as a set.name string, to be able to execute, for example via the /run command */
@@ -352,7 +348,7 @@ export class SlashCommandHandler {
return '';
},
returns: 'updated quick reply',
namedArgumentList: [...qrUpdateArgs, ...qrArgs.map(it=>{
namedArgumentList: [...qrUpdateArgs, ...qrArgs.map(it => {
if (it.name == 'label') {
const clone = SlashCommandNamedArgument.fromProps(it);
clone.isRequired = false;
@@ -691,16 +687,16 @@ export class SlashCommandHandler {
if (!args.from) throw new Error('/import requires from= to be set.');
if (!value) throw new Error('/import requires the unnamed argument to be set.');
let qr = [...this.api.listGlobalSets(), ...this.api.listChatSets()]
.map(it=>this.api.getSetByName(it)?.qrList ?? [])
.map(it => this.api.getSetByName(it)?.qrList ?? [])
.flat()
.find(it=>it.label == args.from)
.find(it => it.label == args.from)
;
if (!qr) {
let [setName, ...qrNameParts] = args.from.split('.');
let qrName = qrNameParts.join('.');
let qrs = QuickReplySet.get(setName);
if (qrs) {
qr = qrs.qrList.find(it=>it.label == qrName);
qr = qrs.qrList.find(it => it.label == qrName);
}
}
if (qr) {
@@ -709,23 +705,23 @@ export class SlashCommandHandler {
if (args._debugController) {
closure.source = args.from;
}
const testCandidates = (executor)=>{
const testCandidates = (executor) => {
return (
executor.namedArgumentList.find(arg=>arg.name == 'key')
executor.namedArgumentList.find(arg => arg.name == 'key')
&& executor.unnamedArgumentList.length > 0
&& executor.unnamedArgumentList[0].value instanceof SlashCommandClosure
) || (
!executor.namedArgumentList.find(arg=>arg.name == 'key')
!executor.namedArgumentList.find(arg => arg.name == 'key')
&& executor.unnamedArgumentList.length > 1
&& executor.unnamedArgumentList[1].value instanceof SlashCommandClosure
);
};
const candidates = closure.executorList
.filter(executor=>['let', 'var'].includes(executor.command.name))
.filter(executor => ['let', 'var'].includes(executor.command.name))
.filter(testCandidates)
.map(executor=>({
key: executor.namedArgumentList.find(arg=>arg.name == 'key')?.value ?? executor.unnamedArgumentList[0].value,
value: executor.unnamedArgumentList[executor.namedArgumentList.find(arg=>arg.name == 'key') ? 0 : 1].value,
.map(executor => ({
key: executor.namedArgumentList.find(arg => arg.name == 'key')?.value ?? executor.unnamedArgumentList[0].value,
value: executor.unnamedArgumentList[executor.namedArgumentList.find(arg => arg.name == 'key') ? 0 : 1].value,
}))
;
for (let i = 0; i < value.length; i++) {
@@ -735,7 +731,7 @@ export class SlashCommandHandler {
dstName = value[i + 2];
i += 2;
}
const pick = candidates.find(it=>it.key == srcName);
const pick = candidates.find(it => it.key == srcName);
if (!pick) throw new Error(`No scoped closure named "${srcName}" found in "${args.from}"`);
if (args._scope.existsVariableInScope(dstName)) {
args._scope.setVariable(dstName, pick.value);
@@ -783,8 +779,6 @@ export class SlashCommandHandler {
}
getSetByName(name) {
const set = this.api.getSetByName(name);
if (!set) {
@@ -802,8 +796,6 @@ export class SlashCommandHandler {
}
async executeQuickReplyByIndex(idx) {
try {
return await this.api.executeQuickReplyByIndex(idx);
@@ -10,15 +10,11 @@ export class ButtonUi {
/**@type {HTMLElement}*/ popoutDom;
constructor(/**@type {QuickReplySettings}*/settings) {
this.settings = settings;
}
render() {
if (this.settings.isPopout) {
return this.renderPopout();
@@ -57,8 +53,6 @@ export class ButtonUi {
}
renderBar() {
if (!this.dom) {
let buttonHolder;
@@ -75,7 +69,7 @@ export class ButtonUi {
popout.classList.add('menu_button');
popout.classList.add('fa-solid');
popout.classList.add('fa-window-restore');
popout.addEventListener('click', ()=>{
popout.addEventListener('click', () => {
this.settings.isPopout = true;
this.refresh();
this.settings.save();
@@ -91,8 +85,8 @@ export class ButtonUi {
}
}
[...this.settings.config.setList, ...(this.settings.chatConfig?.setList ?? []), ...(this.settings.charConfig?.setList ?? [])]
.filter(link=>link.isVisible)
.forEach(link=>buttonHolder.append(link.set.render()))
.filter(link => link.isVisible)
.forEach(link => buttonHolder.append(link.set.render()))
;
}
}
@@ -100,8 +94,6 @@ export class ButtonUi {
}
renderPopout() {
if (!this.popoutDom) {
let buttonHolder;
@@ -130,7 +122,7 @@ export class ButtonUi {
close.classList.add('fa-solid');
close.classList.add('fa-circle-xmark');
close.classList.add('hoverglow');
close.addEventListener('click', ()=>{
close.addEventListener('click', () => {
this.settings.isPopout = false;
this.refresh();
this.settings.save();
@@ -151,8 +143,8 @@ export class ButtonUi {
}
}
[...this.settings.config.setList, ...(this.settings.chatConfig?.setList ?? []), ...(this.settings.charConfig?.setList ?? [])]
.filter(link=>link.isVisible)
.forEach(link=>buttonHolder.append(link.set.render()))
.filter(link => link.isVisible)
.forEach(link => buttonHolder.append(link.set.render()))
;
root.append(body);
}
@@ -29,24 +29,18 @@ export class SettingsUi {
/**@type {HTMLSelectElement}*/ currentSet;
constructor(/**@type {QuickReplySettings}*/settings) {
this.settings = settings;
settings.onRequestEditSet = (qrs) => this.selectQrSet(qrs);
}
rerender() {
if (!this.dom) return;
const content = this.dom.querySelector('.inline-drawer-content');
content.innerHTML = '';
// @ts-ignore
Array.from(this.template.querySelector('.inline-drawer-content').cloneNode(true).children).forEach(el=>{
Array.from(this.template.querySelector('.inline-drawer-content').cloneNode(true).children).forEach(el => {
content.append(el);
});
this.prepareDom();
@@ -75,15 +69,15 @@ export class SettingsUi {
// general settings
this.isEnabled = this.dom.querySelector('#qr--isEnabled');
this.isEnabled.checked = this.settings.isEnabled;
this.isEnabled.addEventListener('click', ()=>this.onIsEnabled());
this.isEnabled.addEventListener('click', () => this.onIsEnabled());
this.isCombined = this.dom.querySelector('#qr--isCombined');
this.isCombined.checked = this.settings.isCombined;
this.isCombined.addEventListener('click', ()=>this.onIsCombined());
this.isCombined.addEventListener('click', () => this.onIsCombined());
this.showPopoutButton = this.dom.querySelector('#qr--showPopoutButton');
this.showPopoutButton.checked = this.settings.showPopoutButton;
this.showPopoutButton.addEventListener('click', ()=>this.onShowPopoutButton());
this.showPopoutButton.addEventListener('click', () => this.onShowPopoutButton());
}
prepareGlobalSetList() {
@@ -131,29 +125,29 @@ export class SettingsUi {
prepareQrEditor() {
// qr editor
this.dom.querySelector('#qr--set-rename').addEventListener('click', async () => this.renameQrSet());
this.dom.querySelector('#qr--set-new').addEventListener('click', async()=>this.addQrSet());
this.dom.querySelector('#qr--set-new').addEventListener('click', async () => this.addQrSet());
/**@type {HTMLInputElement}*/
const importFile = this.dom.querySelector('#qr--set-importFile');
importFile.addEventListener('change', async()=>{
importFile.addEventListener('change', async () => {
await this.importQrSet(importFile.files);
importFile.value = null;
});
this.dom.querySelector('#qr--set-import').addEventListener('click', ()=>importFile.click());
this.dom.querySelector('#qr--set-import').addEventListener('click', () => importFile.click());
this.dom.querySelector('#qr--set-export').addEventListener('click', async () => this.exportQrSet());
this.dom.querySelector('#qr--set-duplicate').addEventListener('click', async () => this.duplicateQrSet());
this.dom.querySelector('#qr--set-delete').addEventListener('click', async()=>this.deleteQrSet());
this.dom.querySelector('#qr--set-add').addEventListener('click', async()=>{
this.dom.querySelector('#qr--set-delete').addEventListener('click', async () => this.deleteQrSet());
this.dom.querySelector('#qr--set-add').addEventListener('click', async () => {
this.currentQrSet.addQuickReply();
});
this.dom.querySelector('#qr--set-paste').addEventListener('click', async()=>{
this.dom.querySelector('#qr--set-paste').addEventListener('click', async () => {
const text = await navigator.clipboard.readText();
this.currentQrSet.addQuickReplyFromText(text);
});
this.dom.querySelector('#qr--set-importQr').addEventListener('click', async()=>{
this.dom.querySelector('#qr--set-importQr').addEventListener('click', async () => {
const inp = document.createElement('input'); {
inp.type = 'file';
inp.accept = '.json';
inp.addEventListener('change', async()=>{
inp.addEventListener('change', async () => {
if (inp.files.length > 0) {
for (const file of inp.files) {
const text = await file.text();
@@ -166,8 +160,8 @@ export class SettingsUi {
});
this.qrList = this.dom.querySelector('#qr--set-qrList');
this.currentSet = this.dom.querySelector('#qr--set');
this.currentSet.addEventListener('change', ()=>this.onQrSetChange());
QuickReplySet.list.toSorted((a,b)=>a.name.toLowerCase().localeCompare(b.name.toLowerCase())).forEach(qrs=>{
this.currentSet.addEventListener('change', () => this.onQrSetChange());
QuickReplySet.list.toSorted((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase())).forEach(qrs => {
const opt = document.createElement('option'); {
opt.value = qrs.name;
opt.textContent = qrs.name;
@@ -175,19 +169,19 @@ export class SettingsUi {
}
});
this.disableSend = this.dom.querySelector('#qr--disableSend');
this.disableSend.addEventListener('click', ()=>{
this.disableSend.addEventListener('click', () => {
const qrs = this.currentQrSet;
qrs.disableSend = this.disableSend.checked;
qrs.save();
});
this.placeBeforeInput = this.dom.querySelector('#qr--placeBeforeInput');
this.placeBeforeInput.addEventListener('click', ()=>{
this.placeBeforeInput.addEventListener('click', () => {
const qrs = this.currentQrSet;
qrs.placeBeforeInput = this.placeBeforeInput.checked;
qrs.save();
});
this.injectInput = this.dom.querySelector('#qr--injectInput');
this.injectInput.addEventListener('click', ()=>{
this.injectInput.addEventListener('click', () => {
const qrs = this.currentQrSet;
qrs.injectInput = this.injectInput.checked;
qrs.save();
@@ -196,7 +190,7 @@ export class SettingsUi {
this.color = this.dom.querySelector('#qr--color');
// @ts-ignore
this.color.color = this.currentQrSet?.color ?? 'transparent';
this.color.addEventListener('change', (evt)=>{
this.color.addEventListener('change', (evt) => {
if (!this.dom.closest('body')) return;
const qrs = this.currentQrSet;
if (initialColorChange) {
@@ -211,7 +205,7 @@ export class SettingsUi {
this.currentQrSet.updateColor();
});
// @ts-ignore
this.dom.querySelector('#qr--colorClear').addEventListener('click', (evt)=>{
this.dom.querySelector('#qr--colorClear').addEventListener('click', (evt) => {
const qrs = this.currentQrSet;
// @ts-ignore
this.color.color = 'transparent';
@@ -219,7 +213,7 @@ export class SettingsUi {
this.currentQrSet.updateColor();
});
this.onlyBorderColor = this.dom.querySelector('#qr--onlyBorderColor');
this.onlyBorderColor.addEventListener('click', ()=>{
this.onlyBorderColor.addEventListener('click', () => {
const qrs = this.currentQrSet;
qrs.onlyBorderColor = this.onlyBorderColor.checked;
qrs.save();
@@ -242,7 +236,7 @@ export class SettingsUi {
$(qrsDom).sortable({
delay: getSortableDelay(),
handle: '.drag-handle',
stop: ()=>this.onQrListSort(),
stop: () => this.onQrListSort(),
});
}
@@ -256,8 +250,6 @@ export class SettingsUi {
}
async onIsEnabled() {
this.settings.isEnabled = this.isEnabled.checked;
this.settings.save();
@@ -274,7 +266,7 @@ export class SettingsUi {
}
async onGlobalSetListSort() {
this.settings.config.setList = Array.from(this.globalSetList.children).map((it,idx)=>{
this.settings.config.setList = Array.from(this.globalSetList.children).map((it, idx) => {
const set = this.settings.config.setList[Number(it.getAttribute('data-order'))];
it.setAttribute('data-order', String(idx));
return set;
@@ -283,7 +275,7 @@ export class SettingsUi {
}
async onChatSetListSort() {
this.settings.chatConfig.setList = Array.from(this.chatSetList.children).map((it,idx)=>{
this.settings.chatConfig.setList = Array.from(this.chatSetList.children).map((it, idx) => {
const set = this.settings.chatConfig.setList[Number(it.getAttribute('data-order'))];
it.setAttribute('data-order', String(idx));
return set;
@@ -292,14 +284,14 @@ export class SettingsUi {
}
updateOrder(list) {
Array.from(list.children).forEach((it,idx)=>{
Array.from(list.children).forEach((it, idx) => {
it.setAttribute('data-order', idx);
});
}
async onQrListSort() {
this.currentQrSet.qrList = Array.from(this.qrList.querySelectorAll('.qr--set-item')).map((it,idx)=>{
const qr = this.currentQrSet.qrList.find(qr=>qr.id == Number(it.getAttribute('data-id')));
this.currentQrSet.qrList = Array.from(this.qrList.querySelectorAll('.qr--set-item')).map((it, idx) => {
const qr = this.currentQrSet.qrList.find(qr => qr.id == Number(it.getAttribute('data-id')));
it.setAttribute('data-order', String(idx));
return qr;
});
@@ -408,7 +400,7 @@ export class SettingsUi {
const qrs = new QuickReplySet();
qrs.name = name;
qrs.addQuickReply();
const idx = QuickReplySet.list.findIndex(it=>it.name.toLowerCase().localeCompare(name.toLowerCase()) == 1);
const idx = QuickReplySet.list.findIndex(it => it.name.toLowerCase().localeCompare(name.toLowerCase()) == 1);
if (idx > -1) {
QuickReplySet.list.splice(idx, 0, qrs);
} else {
@@ -448,7 +440,7 @@ export class SettingsUi {
} else {
/**@type {QuickReplySet}*/
const qrs = QuickReplySet.from(JSON.parse(JSON.stringify(props)));
qrs.qrList = props.qrList.map(it=>QuickReply.from(it));
qrs.qrList = props.qrList.map(it => QuickReply.from(it));
qrs.init();
const oldQrs = QuickReplySet.get(props.name);
if (oldQrs) {
@@ -466,7 +458,7 @@ export class SettingsUi {
this.prepareCharacterSetList();
}
} else {
const idx = QuickReplySet.list.findIndex(it=>it.name.toLowerCase().localeCompare(qrs.name.toLowerCase()) == 1);
const idx = QuickReplySet.list.findIndex(it => it.name.toLowerCase().localeCompare(qrs.name.toLowerCase()) == 1);
if (idx > -1) {
QuickReplySet.list.splice(idx, 0, qrs);
} else {
@@ -496,7 +488,7 @@ export class SettingsUi {
}
exportQrSet() {
const blob = new Blob([JSON.stringify(this.currentQrSet)], { type:'application/json' });
const blob = new Blob([JSON.stringify(this.currentQrSet)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a'); {
a.href = url;
@@ -11,8 +11,6 @@ export class ContextMenu {
/**@type {HTMLElement}*/ menu;
constructor(/**@type {QuickReply}*/qr) {
// this.itemList = items;
this.itemList = this.build(qr).children;
@@ -104,8 +102,6 @@ export class ContextMenu {
}
show({ clientX, clientY }) {
if (this.isActive) return;
this.isActive = true;
@@ -15,8 +15,6 @@ export class MenuItem {
/**@type {function}*/ onExpand;
/**
*
* @param {?string} icon
@@ -80,7 +78,6 @@ export class MenuItem {
}
item.addEventListener('mouseover', () => sub.show(item));
item.addEventListener('mouseleave', () => sub.hide());
}
}
}
@@ -9,8 +9,6 @@ export class SubMenu {
/**@type {HTMLElement}*/ root;
constructor(/**@type {MenuItem[]}*/items) {
this.itemList = items;
}
@@ -29,8 +27,6 @@ export class SubMenu {
}
show(/**@type {HTMLElement}*/parent) {
if (this.isActive) return;
this.isActive = true;
-1
View File
@@ -1031,7 +1031,6 @@ function executeRegexScriptForDebugging(script, text) {
const trailingText = text.substring(lastIndex);
outputText += trailingText;
highlightedOutput += escapeHtml(trailingText);
} catch (e) {
err = (err ? err + '; ' : '') + `Replace error: ${e.message}`;
outputText = text; // Fallback
@@ -115,5 +115,4 @@ jQuery(() => {
returns: 'number of tokens',
helpString: 'Counts the number of tokens in the current chat.',
}));
});
-1
View File
@@ -1043,7 +1043,6 @@ class AllTalkTtsProvider {
// V2: Combine the endpoint with the relative path
return `${this.settings.provider_endpoint}${data.output_file_url}`;
}
} catch (error) {
console.error('[fetchTtsGeneration] Exception caught:', error);
throw error;
@@ -239,7 +239,6 @@ class ChatterboxTtsProvider {
}
this.setupEventListeners();
} catch (error) {
console.error('Error loading Chatterbox settings:', error);
this.updateStatus('Offline');
@@ -518,7 +517,6 @@ class ChatterboxTtsProvider {
});
await audio.play();
} catch (error) {
console.error('Error previewing voice:', error);
this.updateStatus('Ready');
@@ -627,7 +625,6 @@ class ChatterboxTtsProvider {
// Return the response directly - SillyTavern expects a Response object
return response;
} catch (error) {
console.error('Error in generateTts:', error);
this.updateStatus('Ready');
+12 -13
View File
@@ -160,7 +160,7 @@ class CoquiTtsProvider {
.then(response => response.json())
.then(json => {
coquiApiModels = json;
console.debug(DEBUG_PREFIX,'initialized coqui-api model list to', coquiApiModels);
console.debug(DEBUG_PREFIX, 'initialized coqui-api model list to', coquiApiModels);
/*
$('#coqui_api_language')
.find('option')
@@ -180,7 +180,7 @@ class CoquiTtsProvider {
.then(response => response.json())
.then(json => {
coquiApiModelsFull = json;
console.debug(DEBUG_PREFIX,'initialized coqui-api full model list to', coquiApiModelsFull);
console.debug(DEBUG_PREFIX, 'initialized coqui-api full model list to', coquiApiModelsFull);
/*
$('#coqui_api_full_language')
.find('option')
@@ -197,7 +197,7 @@ class CoquiTtsProvider {
}
// Perform a simple readiness check by trying to fetch voiceIds
async checkReady(){
async checkReady() {
throwIfModuleMissing();
await this.fetchTtsVoiceObjects();
}
@@ -384,12 +384,12 @@ class CoquiTtsProvider {
.append('<option value="none">Select model language</option>')
.val('none');
for(let language in coquiApiModels) {
for (let language in coquiApiModels) {
let languageLabel = language;
if (language in languageLabels)
languageLabel = languageLabels[language];
$('#coqui_api_language').append(new Option(languageLabel,language));
console.log(DEBUG_PREFIX,'added language',languageLabel,'(',language,')');
$('#coqui_api_language').append(new Option(languageLabel, language));
console.log(DEBUG_PREFIX, 'added language', languageLabel, '(', language, ')');
}
$('#coqui_api_model_div').show();
@@ -406,12 +406,12 @@ class CoquiTtsProvider {
.append('<option value="none">Select model language</option>')
.val('none');
for(let language in coquiApiModelsFull) {
for (let language in coquiApiModelsFull) {
let languageLabel = language;
if (language in languageLabels)
languageLabel = languageLabels[language];
$('#coqui_api_language').append(new Option(languageLabel,language));
console.log(DEBUG_PREFIX,'added language',languageLabel,'(',language,')');
$('#coqui_api_language').append(new Option(languageLabel, language));
console.log(DEBUG_PREFIX, 'added language', languageLabel, '(', language, ')');
}
$('#coqui_api_model_div').show();
@@ -450,8 +450,8 @@ class CoquiTtsProvider {
if (model_origin == 'coqui-api-full')
modelDict = coquiApiModelsFull;
for(let model_dataset in modelDict[model_language])
for(let model_name in modelDict[model_language][model_dataset]) {
for (let model_dataset in modelDict[model_language])
for (let model_name in modelDict[model_language][model_dataset]) {
const model_id = model_dataset + '/' + model_name;
const model_label = model_name + ' (' + model_dataset + ' dataset)';
$('#coqui_api_model_name').append(new Option(model_label, model_id));
@@ -526,7 +526,7 @@ class CoquiTtsProvider {
// Check if already installed and propose to do it otherwise
const model_id = modelDict[model_language][model_dataset][model_name].id;
console.debug(DEBUG_PREFIX,'Check if model is already installed',model_id);
console.debug(DEBUG_PREFIX, 'Check if model is already installed', model_id);
const result = await CoquiTtsProvider.checkmodel_state(model_id);
const resultJSON = await result.json();
const model_state = resultJSON.model_state;
@@ -583,7 +583,6 @@ class CoquiTtsProvider {
$('#coqui_api_model_install_button').show();
return;
}
}
@@ -110,15 +110,11 @@ class CosyVoiceProvider {
//#################//
async getVoice(voiceName) {
if (this.voices.length == 0) {
this.voices = await this.fetchTtsVoiceObjects();
}
const match = this.voices.filter(
v => v.name == voiceName,
)[0];
@@ -130,7 +126,6 @@ class CosyVoiceProvider {
}
async generateTts(text, voiceId) {
const response = await this.fetchTtsGeneration(text, voiceId);
return response;
@@ -198,7 +193,6 @@ class CosyVoiceProvider {
}
// Interface not used
async fetchTtsFromHistory(history_item_id) {
return Promise.resolve(history_item_id);
+1 -1
View File
@@ -132,7 +132,7 @@ class ElevenLabsTtsProvider {
}
if (Object.hasOwn(settings, 'apiKey')) {
if (settings.apiKey && !secret_state[SECRET_KEYS.ELEVENLABS]){
if (settings.apiKey && !secret_state[SECRET_KEYS.ELEVENLABS]) {
await writeSecret(SECRET_KEYS.ELEVENLABS, settings.apiKey);
}
delete settings.apiKey;
@@ -124,7 +124,6 @@ export class GoogleNativeTtsProvider {
console.info(`Google TTS: Loaded ${this.voices.length} voices`);
return this.voices;
} catch (error) {
console.error('Failed to fetch Google TTS voices:', error);
throw error;
@@ -151,7 +150,6 @@ export class GoogleNativeTtsProvider {
this.audioElement.src = url;
this.audioElement.play();
this.audioElement.onended = () => URL.revokeObjectURL(url);
} catch (error) {
console.error('TTS Preview Error:', error);
toastr.error(`Could not generate preview: ${error.message}`);
@@ -115,15 +115,11 @@ class GptSovitsV2Provider {
//#################//
async getVoice(voiceName) {
if (this.voices.length == 0) {
this.voices = await this.fetchTtsVoiceObjects();
}
const match = this.voices.filter(
v => v.name == voiceName,
)[0];
@@ -135,7 +131,6 @@ class GptSovitsV2Provider {
}
async generateTts(text, voiceId) {
const response = await this.fetchTtsGeneration(text, voiceId);
return response;
@@ -171,8 +166,6 @@ class GptSovitsV2Provider {
*/
async fetchTtsGeneration(inputText, voiceId, lang = null, forceNoStreaming = false) {
console.info(`Generating new TTS for voice_id ${voiceId}`);
@@ -215,7 +208,6 @@ class GptSovitsV2Provider {
}
// Interface not used
async fetchTtsFromHistory(history_item_id) {
return Promise.resolve(history_item_id);
-9
View File
@@ -1,4 +1,3 @@
import { saveTtsProviderSettings } from './index.js';
export { GSVITtsProvider };
@@ -60,11 +59,9 @@ class GSVITtsProvider {
const characterList = await response.json();
this.characterList = characterList;
this.voices = Object.keys(characterList);
}
get settingsHtml() {
let html = `
<label for="gsvi_api_language">Text Language</label>
@@ -142,11 +139,8 @@ class GSVITtsProvider {
$('#gsvi_batch_size_output').text(this.settings.batch_size);
// Persist settings changes
saveTtsProviderSettings();
}
async loadSettings(settings) {
@@ -197,7 +191,6 @@ class GSVITtsProvider {
}
// Perform a simple readiness check by trying to fetch voiceIds
async checkReady() {
await Promise.allSettled([this.fetchCharacterList()]);
@@ -256,12 +249,10 @@ class GSVITtsProvider {
return `${this.settings.provider_endpoint}/tts?${params.toString()}`;
}
// Interface not used by GSVI TTS
async fetchTtsFromHistory(history_item_id) {
return Promise.resolve(history_item_id);
}
}
-4
View File
@@ -611,7 +611,6 @@ async function processTtsQueue() {
// Pass the full voiceMapKey (e.g., "User ("Quotes")") as well with character name
await tts(segmentText, voiceId, char, voiceMapKey);
} catch (error) {
toastr.error(error.toString());
console.error(error);
@@ -707,7 +706,6 @@ async function processTtsQueue() {
// Clear current job so the segmented jobs can be processed
currentTtsJob = null;
} catch (error) {
toastr.error(error.toString());
console.error(error);
@@ -1286,7 +1284,6 @@ export function getCharacters(unrestricted) {
}
return characters;
}
export function sanitizeId(input) {
@@ -1314,7 +1311,6 @@ function parseVoiceMap(voiceMapString) {
}
/**
* Apply voiceMap based on current voiceMapEntries
*/
@@ -7,7 +7,7 @@ let ready = false;
let voices = [];
// Handle messages from the main thread
self.onmessage = async function(e) {
self.onmessage = async function (e) {
const { action, data } = e.data;
switch (action) {
-2
View File
@@ -837,7 +837,6 @@ class MiniMaxTtsProvider {
// Backend handles all the complex processing and returns audio data directly
console.debug('MiniMax TTS: Audio response received from backend');
return response;
} catch (error) {
console.error('Error in MiniMax TTS generation:', error);
throw error;
@@ -954,7 +953,6 @@ class MiniMaxTtsProvider {
this.audioElement.onended = null;
this.audioElement.onerror = null;
};
} catch (error) {
console.error('MiniMax TTS Preview Error:', error);
toastr.error(`Could not generate preview: ${error.message}`);
-1
View File
@@ -146,7 +146,6 @@ class OpenAITtsProvider {
}
populateCharacterInstructions() {
const currentCharacters = $('.tts_voicemap_block_char span').map((i, el) => $(el).text()).get();
$('#openai-character-instructions').empty();
@@ -172,5 +172,4 @@ class SileroTtsProvider {
async fetchTtsFromHistory(history_item_id) {
return Promise.resolve(history_item_id);
}
}
-1
View File
@@ -323,5 +323,4 @@ class XTTSTtsProvider {
async fetchTtsFromHistory(history_item_id) {
return Promise.resolve(history_item_id);
}
}
@@ -1456,7 +1456,6 @@ async function onViewStatsClick() {
messageElement.addClass('vectorized');
}
}
}
async function onVectorizeAllFilesClick() {
-1
View File
@@ -13,7 +13,6 @@ export function SaveLocal(target, val) {
export function LoadLocal(target) {
console.debug('LoadLocal -- ' + target);
return localStorage.getItem(target);
}
/**
* @deprecated THIS FUNCTION IS OBSOLETE. DO NOT USE
-1
View File
@@ -76,7 +76,6 @@ export const fuzzySearchCategories = Object.freeze({
* data = filterHelper.applyFilters(data);
*/
export class FilterHelper {
/**
* Cache fuzzy search weighting scores for re-usability, sorting and stuff
*
-1
View File
@@ -2477,7 +2477,6 @@ jQuery(() => {
const value = $(this).prop('checked');
hideMutedSprites = value;
onHideMutedSpritesClick(value);
});
$('#send_textarea').on('keyup', onSendTextareaInput);
$('#groupCurrentMemberPopoutButton').on('click', doCurMemberListPopout);
+4 -5
View File
@@ -59,7 +59,8 @@ export function initInputMarkdown() {
let cursorShift = charsToAdd.length;
let selectedTextandPossibleFormatting = textarea.value.substring(start - possiblePreviousFormattingMargin, end + possiblePreviousFormattingMargin).trim();
if (isTextSelected) { //if text is selected
if (isTextSelected) {
//if text is selected
selectedText = textarea.value.substring(start, end);
if (selectedTextandPossibleFormatting === charsToAdd + selectedText + charsToAdd) {
// If the selected text is already formatted, remove the formatting
@@ -90,7 +91,8 @@ export function initInputMarkdown() {
textarea.focus();
document.execCommand('insertText', false, charsToAdd + selectedText + charsToAdd + possibleAddedSpace);
}
} else {// No text is selected
} else {
// No text is selected
//check 1 character before and after the cursor for non-space characters
if (beforeCaret !== ' ' && afterCaret !== ' ' && afterCaret !== '' && beforeCaret !== '') { //look for caret in the middle of a word
@@ -116,7 +118,6 @@ export function initInputMarkdown() {
}
if (charsToAdd + discoveredWord + charsToAdd === discoveredWordWithPossibleFormatting) {
// Replace the expanded selection with the original discovered word
textarea.focus();
document.execCommand('insertText', false, discoveredWord);
@@ -126,8 +127,6 @@ export function initInputMarkdown() {
textarea.focus();
document.execCommand('insertText', false, charsToAdd + discoveredWord + charsToAdd);
}
} else { //caret is not inside a word, so just add the formatting
textarea.focus();
textarea.setSelectionRange(start, end);
-1
View File
@@ -798,7 +798,6 @@ jQuery(() => {
$('#instruct_system_sequence').prop('readOnly', false);
$('#instruct_system_suffix').prop('readOnly', false);
}
});
$('#instruct_enabled').on('change', function () {
-4
View File
@@ -2279,7 +2279,6 @@ function appendElectronHubOptions(model_list, groupModels = false) {
appendOption(model);
});
}
}
function electronHubSortBy(data, property = 'alphabetically') {
@@ -3985,7 +3984,6 @@ function loadOpenAISettings(data, settings) {
option.value = i;
option.text = item;
$('#settings_preset_openai').append(option);
});
openai_setting_names = settingNames;
@@ -4896,7 +4894,6 @@ function getSiliconflowMaxContext(model, isUnlocked) {
// Return context size if model found, otherwise default to 32k
return Object.entries(contextMap).find(([key]) => model.includes(key))?.[1] || max_32k;
}
/**
@@ -5041,7 +5038,6 @@ async function onModelChange() {
console.log('Claude model changed to', value);
oai_settings.claude_model = value;
$('#model_claude_select').val(oai_settings.claude_model);
}
if ($(this).is('#model_openai_select')) {
-2
View File
@@ -1593,7 +1593,6 @@ export async function showCharConnections() {
highlightPersonas: true,
targetedChar: getCurrentConnectionObj(),
shiftClickHandler: (element, ev) => {
const personaId = $(element).attr('data-pid');
/** @type {PersonaConnection[]} */
@@ -1845,7 +1844,6 @@ async function lockPersonaCallback(_args, value) {
if (isFalseBoolean(value)) {
await setPersonaLockState(false, type);
return 'false';
}
return '';
-1
View File
@@ -478,7 +478,6 @@ export class Popup {
break;
}
}
};
this.dlg.addEventListener('keydown', keyListener.bind(this));
}
+1 -8
View File
@@ -536,7 +536,6 @@ function switchSwipeNumAllMessages() {
var originalSliderValues = [];
async function switchLabMode({ noReset = false } = {}) {
/* if (power_user.enableZenSliders && power_user.enableLabMode) {
toastr.warning("Can't start Lab Mode while Zen Sliders are active")
return
@@ -571,8 +570,6 @@ async function switchLabMode({ noReset = false } = {}) {
$('#amount_gen').attr('min', '1')
.attr('max', '99999')
.attr('step', '1');
} else if (!noReset) {
//re apply the original sliders values to each input
originalSliderValues.forEach(function (slider) {
@@ -628,7 +625,6 @@ async function switchZenSliders() {
});
$('div[id$="_zenslider"]').remove();
}
}
async function CreateZenSliders(elmnt) {
var originalSlider = elmnt;
@@ -1178,7 +1174,6 @@ function applyShadowWidth() {
document.documentElement.style.setProperty('--shadowWidth', String(power_user.shadow_width));
$('#shadow_width_counter').val(power_user.shadow_width);
$('#shadow_width').val(power_user.shadow_width);
}
function applyFontScale(type) {
@@ -2954,7 +2949,6 @@ function setAvgBG() {
} */
function getAverageRGB(imgEl) {
var blockSize = 5, // only visit every 5 pixels
defaultRGB = { r: 0, g: 0, b: 0 }, // for non-supporting envs
canvas = document.createElement('canvas'),
@@ -2994,7 +2988,6 @@ function setAvgBG() {
rgb.b = ~~(rgb.b / count);
return rgb;
}
/**
@@ -4035,7 +4028,7 @@ jQuery(() => {
return;
}
eventSource.once(event_types.SETTINGS_UPDATED, function() {
eventSource.once(event_types.SETTINGS_UPDATED, function () {
toastr.warning(
t`Click here to reload.`,
t`Toggling the Experimental Macro Engine requires a reload.`,
-1
View File
@@ -514,7 +514,6 @@ class PresetManager {
console.error('Preset could not be renamed', error);
throw new Error('Preset could not be renamed');
}
}
/**
-1
View File
@@ -865,7 +865,6 @@ function selectReasoningTemplateCallback(args, name) {
UI.$select.val(foundName).trigger('change');
!quiet && toastr.success(`Reasoning template "${foundName}" selected`);
return foundName;
}
function registerReasoningSlashCommands() {
-1
View File
@@ -203,7 +203,6 @@ function setSamplerListListeners() {
console.log(samplerName, relatedDOMElement.data(SELECT_SAMPLER.DATA), shouldDisplay);
});
}
function isElementVisibleInDOM(element) {
+1 -1
View File
@@ -9,7 +9,7 @@ export const markdownUnderscoreExt = () => {
return [{
type: 'output',
regex: new RegExp('(<code(?:\\s+[^>]*)?>[\\s\\S]*?<\\/code>|<style(?:\\s+[^>]*)?>[\\s\\S]*?<\\/style>)|\\b(?<!_)_(?!_)(.*?)(?<!_)_(?!_)\\b', 'gi'),
replace: function(match, tagContent, italicContent) {
replace: function (match, tagContent, italicContent) {
if (tagContent) {
// If it's inside <code> or <style> tags, return unchanged
return match;
-2
View File
@@ -4916,7 +4916,6 @@ export async function sendNarratorMessage(args, text) {
}
export async function promptQuietForLoudResponse(who, text) {
let character_id = getContext().characterId;
if (who === 'sys') {
text = 'System: ' + text;
@@ -4955,7 +4954,6 @@ export async function promptQuietForLoudResponse(who, text) {
addOneMessage(message);
await eventSource.emit(event_types.USER_MESSAGE_RENDERED, (chat.length - 1));
await saveChatConditional();
}
async function sendCommentMessage(args, text) {
@@ -26,7 +26,6 @@ import { SlashCommandScope } from './SlashCommandScope.js';
*/
export class SlashCommand {
/**
* Creates a SlashCommand from a properties object.
@@ -48,8 +47,6 @@ export class SlashCommand {
}
/**@type {string}*/ name;
/**@type {(namedArguments:NamedArguments, unnamedArguments:UnnamedArguments)=>string|SlashCommandClosure|Promise<string|SlashCommandClosure>}*/ callback;
/**@type {string}*/ helpString;
@@ -86,7 +83,7 @@ export class SlashCommand {
name.classList.add('name');
name.classList.add('monospace');
name.textContent = '/';
key.split('').forEach(char=>{
key.split('').forEach(char => {
const span = document.createElement('span'); {
span.textContent = char;
name.append(span);
@@ -229,7 +226,7 @@ export class SlashCommand {
const unnamedArguments = cmd.unnamedArgumentList ?? [];
const returnType = cmd.returns ?? 'void';
const helpString = cmd.helpString ?? 'NO DETAILS';
const aliasList = [cmd.name, ...(cmd.aliases ?? [])].filter(it=>it != key);
const aliasList = [cmd.name, ...(cmd.aliases ?? [])].filter(it => it != key);
const specs = document.createElement('div'); {
specs.classList.add('specs');
const head = document.createElement('div'); {
@@ -257,7 +254,7 @@ export class SlashCommand {
this.isExtension ? 'Extension' : 'Core',
this.isThirdParty ? 'Third Party' : (this.isExtension ? 'Core' : null),
this.source,
].filter(it=>it).join('\n');
].filter(it => it).join('\n');
head.append(src);
}
if (this.rawQuotes) {
@@ -5,7 +5,6 @@ import { SlashCommandExecutor } from './SlashCommandExecutor.js';
import { SlashCommandScope } from './SlashCommandScope.js';
/**@readonly*/
/**@enum {string}*/
export const ARGUMENT_TYPE = {
@@ -68,7 +67,7 @@ export class SlashCommandArgument {
this.isRequired = isRequired ?? false;
this.acceptsMultiple = acceptsMultiple ?? false;
this.defaultValue = defaultValue;
this.enumList = (enums ? Array.isArray(enums) ? enums : [enums] : []).map(it=>{
this.enumList = (enums ? Array.isArray(enums) ? enums : [enums] : []).map(it => {
if (it instanceof SlashCommandEnumValue) return it;
return new SlashCommandEnumValue(it);
});
@@ -22,11 +22,11 @@ export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult {
executor.start,
Object
.keys(commands)
.map(key=>new SlashCommandCommandAutoCompleteOption(commands[key], key))
.map(key => new SlashCommandCommandAutoCompleteOption(commands[key], key))
,
false,
()=>`No matching slash commands for "/${this.name}"`,
()=>'No slash commands found!',
() => `No matching slash commands for "/${this.name}"`,
() => 'No slash commands found!',
);
this.executor = executor;
this.scope = scope;
@@ -63,7 +63,7 @@ export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult {
if (!Array.isArray(this.executor.command?.namedArgumentList)) {
return null;
}
const notProvidedNamedArguments = this.executor.command.namedArgumentList.filter(arg=>!this.executor.namedArgumentList.find(it=>it.name == arg.name));
const notProvidedNamedArguments = this.executor.command.namedArgumentList.filter(arg => !this.executor.namedArgumentList.find(it => it.name == arg.name));
let name;
let value;
let start;
@@ -73,13 +73,13 @@ export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult {
const namedArgsFollowedBySpace = text[this.executor.endNamedArgs] == ' ';
if (this.executor.startNamedArgs <= index && this.executor.endNamedArgs + (namedArgsFollowedBySpace ? 1 : 0) >= index) {
// cursor is somewhere within the named arguments (including final space)
argAssign = this.executor.namedArgumentList.find(it=>it.start <= index && it.end >= index);
argAssign = this.executor.namedArgumentList.find(it => it.start <= index && it.end >= index);
if (argAssign) {
const [argName, ...v] = text.slice(argAssign.start, index).split(getSplitRegex());
name = argName;
value = v.join('');
start = argAssign.start;
cmdArg = this.executor.command.namedArgumentList.find(it=>[it.name, `${it.name}=`].includes(argAssign.name));
cmdArg = this.executor.command.namedArgumentList.find(it => [it.name, `${it.name}=`].includes(argAssign.name));
if (cmdArg) notProvidedNamedArguments.push(cmdArg);
} else {
name = '';
@@ -106,13 +106,13 @@ export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult {
// if cursor is already behind "=" check for enums
const enumList = cmdArg?.enumProvider?.(this.executor, this.scope) ?? cmdArg?.enumList;
if (cmdArg && enumList?.length) {
if (isSelect && enumList.find(it=>it.value == value) && argAssign && argAssign.end == index) {
if (isSelect && enumList.find(it => it.value == value) && argAssign && argAssign.end == index) {
return null;
}
const result = new AutoCompleteSecondaryNameResult(
value,
start + name.length,
enumList.map(it=>SlashCommandEnumAutoCompleteOption.from(this.executor.command, it)),
enumList.map(it => SlashCommandEnumAutoCompleteOption.from(this.executor.command, it)),
true,
);
result.isRequired = true;
@@ -125,10 +125,10 @@ export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult {
const result = new AutoCompleteSecondaryNameResult(
name,
start,
notProvidedNamedArguments.map(it=>new SlashCommandNamedArgumentAutoCompleteOption(it, this.executor.command)),
notProvidedNamedArguments.map(it => new SlashCommandNamedArgumentAutoCompleteOption(it, this.executor.command)),
false,
);
result.isRequired = notProvidedNamedArguments.find(it=>it.isRequired) != null;
result.isRequired = notProvidedNamedArguments.find(it => it.isRequired) != null;
return result;
}
@@ -147,7 +147,7 @@ export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult {
let argAssign;
if (this.executor.startUnnamedArgs <= index && this.executor.endUnnamedArgs + 1 >= index) {
// cursor is somwehere in the unnamed args
const idx = this.executor.unnamedArgumentList.findIndex(it=>it.start <= index && it.end >= index);
const idx = this.executor.unnamedArgumentList.findIndex(it => it.start <= index && it.end >= index);
if (idx > -1) {
argAssign = this.executor.unnamedArgumentList[idx];
cmdArg = this.executor.command.unnamedArgumentList[idx];
@@ -179,10 +179,10 @@ export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult {
const result = new AutoCompleteSecondaryNameResult(
value,
start,
enumList.map(it=>SlashCommandEnumAutoCompleteOption.from(this.executor.command, it)),
enumList.map(it => SlashCommandEnumAutoCompleteOption.from(this.executor.command, it)),
false,
);
const isCompleteValue = enumList.find(it=>it.value == value);
const isCompleteValue = enumList.find(it => it.value == value);
const isSelectedValue = isSelect && isCompleteValue;
result.isRequired = cmdArg.isRequired && !isSelectedValue;
result.forceMatch = cmdArg.forceEnum;
@@ -25,7 +25,7 @@ export class SlashCommandBrowser {
inp.classList.add('text_pole');
inp.type = 'search';
inp.placeholder = 'Search slash commands - use quotes to search "literal" instead of fuzzy';
inp.addEventListener('input', ()=>{
inp.addEventListener('input', () => {
this.details?.remove();
this.details = null;
let query = inp.value.trim();
@@ -38,7 +38,7 @@ export class SlashCommandBrowser {
const match = queryRegex.exec(query);
if (!match) break;
if (match[1] !== undefined) {
fuzzyList.push(new RegExp(`^(.*?)${match[1].split('').map(char=>`(${escapeRegex(char)})`).join('(.*?)')}(.*?)$`, 'i'));
fuzzyList.push(new RegExp(`^(.*?)${match[1].split('').map(char => `(${escapeRegex(char)})`).join('(.*?)')}(.*?)$`, 'i'));
} else if (match[2] !== undefined) {
quotedList.push(match[2]);
}
@@ -47,17 +47,17 @@ export class SlashCommandBrowser {
for (const cmd of this.cmdList) {
const targets = [
cmd.name,
...cmd.namedArgumentList.map(it=>it.name),
...cmd.namedArgumentList.map(it=>it.description),
...cmd.namedArgumentList.map(it=>it.enumList.map(e=>e.value)).flat(),
...cmd.namedArgumentList.map(it=>it.typeList).flat(),
...cmd.unnamedArgumentList.map(it=>it.description),
...cmd.unnamedArgumentList.map(it=>it.enumList.map(e=>e.value)).flat(),
...cmd.unnamedArgumentList.map(it=>it.typeList).flat(),
...cmd.namedArgumentList.map(it => it.name),
...cmd.namedArgumentList.map(it => it.description),
...cmd.namedArgumentList.map(it => it.enumList.map(e => e.value)).flat(),
...cmd.namedArgumentList.map(it => it.typeList).flat(),
...cmd.unnamedArgumentList.map(it => it.description),
...cmd.unnamedArgumentList.map(it => it.enumList.map(e => e.value)).flat(),
...cmd.unnamedArgumentList.map(it => it.typeList).flat(),
...cmd.aliases,
cmd.helpString,
];
const find = ()=>targets.find(t=>(fuzzyList.find(f=>f.test(t)) ?? quotedList.find(q=>t.includes(q))) !== undefined) !== undefined;
const find = () => targets.find(t => (fuzzyList.find(f => f.test(t)) ?? quotedList.find(q => t.includes(q))) !== undefined) !== undefined;
if (fuzzyList.length + quotedList.length === 0 || find()) {
this.itemMap[cmd.name].classList.remove('isFiltered');
} else {
@@ -85,7 +85,7 @@ export class SlashCommandBrowser {
const item = cmd.renderHelpItem();
this.itemMap[cmd.name] = item;
let details;
item.addEventListener('click', ()=>{
item.addEventListener('click', () => {
if (!details) {
details = document.createElement('div'); {
details.classList.add('autoComplete-detailsWrap');
@@ -97,7 +97,7 @@ export class SlashCommandBrowser {
}
}
if (this.details !== details) {
Array.from(list.querySelectorAll('.selected')).forEach(it=>it.classList.remove('selected'));
Array.from(list.querySelectorAll('.selected')).forEach(it => it.classList.remove('selected'));
item.classList.add('selected');
this.details?.remove();
container.append(details);
@@ -122,13 +122,13 @@ export class SlashCommandBrowser {
}
parent.append(this.dom);
this.mo = new MutationObserver(muts=>{
if (muts.find(mut=>Array.from(mut.removedNodes).find(it=>it === this.dom || it.contains(this.dom)))) {
this.mo = new MutationObserver(muts => {
if (muts.find(mut => Array.from(mut.removedNodes).find(it => it === this.dom || it.contains(this.dom)))) {
this.mo.disconnect();
window.removeEventListener('keydown', boundHandler);
}
});
this.mo.observe(document.querySelector('#chat'), { childList:true, subtree:true });
this.mo.observe(document.querySelector('#chat'), { childList: true, subtree: true });
const boundHandler = this.handleKeyDown.bind(this);
window.addEventListener('keydown', boundHandler);
return this.dom;
@@ -37,7 +37,7 @@ export class SlashCommandClosure {
/**@type {number}*/
get commandCount() {
return this.executorList.map(executor=>executor.commandCount).reduce((sum,cur)=>sum + cur, 0);
return this.executorList.map(executor => executor.commandCount).reduce((sum, cur) => sum + cur, 0);
}
constructor(parent) {
@@ -156,7 +156,7 @@ export class SlashCommandClosure {
let isList = false;
let listValues = [];
scope = scope ?? this.scope;
const escapeMacro = (it, isAnchored = false)=>{
const escapeMacro = (it, isAnchored = false) => {
const regexText = escapeRegex(it.key.replace(/\*/g, '~~~WILDCARD~~~'))
.replaceAll('~~~WILDCARD~~~', '(?:(?:(?!(?:::|}})).)*)')
;
@@ -165,7 +165,7 @@ export class SlashCommandClosure {
}
return regexText;
};
const macroList = scope.macroList.toSorted((a,b)=>{
const macroList = scope.macroList.toSorted((a, b) => {
if (a.key.includes('*') && !b.key.includes('*')) return 1;
if (!a.key.includes('*') && b.key.includes('*')) return -1;
if (a.key.includes('*') && b.key.includes('*')) return b.key.indexOf('*') - a.key.indexOf('*');
@@ -174,7 +174,7 @@ export class SlashCommandClosure {
if (power_user.experimental_macro_engine) {
return this.substituteWithMacroEngine(text, scope, macroList);
}
const macros = macroList.map(it=>escapeMacro(it)).join('|');
const macros = macroList.map(it => escapeMacro(it)).join('|');
const re = new RegExp(`(?<pipe>{{pipe}})|(?:{{var::(?<var>[^\\s]+?)(?:::(?<varIndex>(?!}}).+))?}})|(?:{{(?<macro>${macros})}})`);
let done = '';
let remaining = text;
@@ -182,7 +182,7 @@ export class SlashCommandClosure {
const match = re.exec(remaining);
const before = substituteParams(remaining.slice(0, match.index));
const after = remaining.slice(match.index + match[0].length);
const replacer = match.groups.pipe ? scope.pipe : match.groups.var ? scope.getVariable(match.groups.var, match.groups.index) : macroList.find(it=>it.key == match.groups.macro || new RegExp(escapeMacro(it, true)).test(match.groups.macro))?.value;
const replacer = match.groups.pipe ? scope.pipe : match.groups.var ? scope.getVariable(match.groups.var, match.groups.index) : macroList.find(it => it.key == match.groups.macro || new RegExp(escapeMacro(it, true)).test(match.groups.macro))?.value;
if (replacer instanceof SlashCommandClosure) {
replacer.abortController = this.abortController;
replacer.breakController = this.breakController;
@@ -253,7 +253,7 @@ export class SlashCommandClosure {
return step.value;
}
async * executeDirect() {
async* executeDirect() {
this.debugController?.down(this);
// closure arguments
for (const arg of this.argumentList) {
@@ -325,10 +325,10 @@ export class SlashCommandClosure {
// breakpoint has to yield before arguments are resolved if one of the
// arguments is an immediate closure, otherwise you cannot step into the
// immediate closure
const hasImmediateClosureInNamedArgs = /**@type {SlashCommandExecutor}*/(step.value)?.namedArgumentList?.find(it=>it.value instanceof SlashCommandClosure && it.value.executeNow);
const hasImmediateClosureInUnnamedArgs = /**@type {SlashCommandExecutor}*/(step.value)?.unnamedArgumentList?.find(it=>it.value instanceof SlashCommandClosure && it.value.executeNow);
const hasImmediateClosureInNamedArgs = /**@type {SlashCommandExecutor}*/(step.value)?.namedArgumentList?.find(it => it.value instanceof SlashCommandClosure && it.value.executeNow);
const hasImmediateClosureInUnnamedArgs = /**@type {SlashCommandExecutor}*/(step.value)?.unnamedArgumentList?.find(it => it.value instanceof SlashCommandClosure && it.value.executeNow);
if (hasImmediateClosureInNamedArgs || hasImmediateClosureInUnnamedArgs) {
this.debugController.isStepping = yield { closure:this, executor:step.value };
this.debugController.isStepping = yield { closure: this, executor: step.value };
} else {
this.debugController.isStepping = true;
this.debugController.stepStack[this.debugController.stepStack.length - 1] = true;
@@ -338,10 +338,10 @@ export class SlashCommandClosure {
this.debugController.isSteppingInto = false;
// if stepping, have to yield before arguments are resolved if one of the arguments
// is an immediate closure, otherwise you cannot step into the immediate closure
const hasImmediateClosureInNamedArgs = /**@type {SlashCommandExecutor}*/(step.value)?.namedArgumentList?.find(it=>it.value instanceof SlashCommandClosure && it.value.executeNow);
const hasImmediateClosureInUnnamedArgs = /**@type {SlashCommandExecutor}*/(step.value)?.unnamedArgumentList?.find(it=>it.value instanceof SlashCommandClosure && it.value.executeNow);
const hasImmediateClosureInNamedArgs = /**@type {SlashCommandExecutor}*/(step.value)?.namedArgumentList?.find(it => it.value instanceof SlashCommandClosure && it.value.executeNow);
const hasImmediateClosureInUnnamedArgs = /**@type {SlashCommandExecutor}*/(step.value)?.unnamedArgumentList?.find(it => it.value instanceof SlashCommandClosure && it.value.executeNow);
if (hasImmediateClosureInNamedArgs || hasImmediateClosureInUnnamedArgs) {
this.debugController.isStepping = yield { closure:this, executor:step.value };
this.debugController.isStepping = yield { closure: this, executor: step.value };
}
}
// resolve args
@@ -354,7 +354,7 @@ export class SlashCommandClosure {
}
} else if (!step.done && this.debugController?.testStepping(this)) {
this.debugController.isSteppingInto = false;
this.debugController.isStepping = yield { closure:this, executor:step.value };
this.debugController.isStepping = yield { closure: this, executor: step.value };
}
// execute executor
step = await stepper.next();
@@ -377,7 +377,7 @@ export class SlashCommandClosure {
* - after arguments are resolved
* - after execution
*/
async * executeStep() {
async* executeStep() {
let done = 0;
let isFirst = true;
for (const executor of this.executorList) {
@@ -429,7 +429,7 @@ export class SlashCommandClosure {
// then yield for "before exec"
yield executor;
// followed by command execution
executor.onProgress = (subDone, subTotal)=>this.onProgress?.(done + subDone, this.commandCount);
executor.onProgress = (subDone, subTotal) => this.onProgress?.(done + subDone, this.commandCount);
const isStepping = this.debugController?.testStepping(this);
if (this.debugController) {
this.debugController.isStepping = false || this.debugController.isSteppingInto;
@@ -586,7 +586,7 @@ export class SlashCommandClosure {
if (!executor.command.splitUnnamedArgument) {
if (value.length == 1) {
value = value[0];
} else if (!value.find(it=>it instanceof SlashCommandClosure)) {
} else if (!value.find(it => it instanceof SlashCommandClosure)) {
value = value.join('');
}
}
@@ -598,7 +598,7 @@ export class SlashCommandClosure {
?.replace(/\\\}/g, '}')
;
} else if (Array.isArray(value)) {
value = value.map(v=>{
value = value.map(v => {
if (typeof v == 'string') {
return v
?.replace(/\\\{/g, '{')
@@ -10,8 +10,6 @@ export class SlashCommandCommandAutoCompleteOption extends AutoCompleteOption {
}
/**
* @param {SlashCommand} command
* @param {string} name
@@ -155,7 +155,7 @@ export const commonEnumProviders = {
...isAll || types.includes('scope') ? scope.allVariableNames.map(name => new SlashCommandEnumValue(name, null, enumTypes.variable, enumIcons.scopeVariable)) : [],
...isAll || types.includes('local') ? Object.keys(chat_metadata.variables ?? []).map(name => new SlashCommandEnumValue(name, null, enumTypes.name, enumIcons.localVariable)) : [],
...isAll || types.includes('global') ? Object.keys(extension_settings.variables.global ?? []).map(name => new SlashCommandEnumValue(name, null, enumTypes.macro, enumIcons.globalVariable)) : [],
].filter((item, idx, list)=>idx == list.findIndex(it=>it.value == item.value));
].filter((item, idx, list) => idx == list.findIndex(it => it.value == item.value));
},
/**
@@ -18,15 +18,11 @@ export class SlashCommandDebugController {
/** @type {(closure:SlashCommandClosure, executor:SlashCommandExecutor)=>Promise<boolean>} */ onBreakPoint;
testStepping(closure) {
return this.stepStack[this.stack.indexOf(closure)];
}
down(closure) {
this.stack.push(closure);
if (this.stepStack.length < this.stack.length) {
@@ -44,20 +40,19 @@ export class SlashCommandDebugController {
}
resume() {
this.continueResolver?.(false);
this.continuePromise = null;
this.stepStack.forEach((_,idx)=>this.stepStack[idx] = false);
this.stepStack.forEach((_, idx) => this.stepStack[idx] = false);
}
step() {
this.stepStack.forEach((_,idx)=>this.stepStack[idx] = true);
this.stepStack.forEach((_, idx) => this.stepStack[idx] = true);
this.continueResolver?.(true);
this.continuePromise = null;
}
stepInto() {
this.isSteppingInto = true;
this.stepStack.forEach((_,idx)=>this.stepStack[idx] = true);
this.stepStack.forEach((_, idx) => this.stepStack[idx] = true);
this.continueResolver?.(true);
this.continuePromise = null;
}
@@ -69,7 +64,7 @@ export class SlashCommandDebugController {
}
async awaitContinue() {
this.continuePromise ??= new Promise(resolve=>{
this.continuePromise ??= new Promise(resolve => {
this.continueResolver = resolve;
});
this.isStepping = await this.continuePromise;
@@ -9,7 +9,7 @@ export class SlashCommandEnumAutoCompleteOption extends AutoCompleteOption {
* @returns {SlashCommandEnumAutoCompleteOption}
*/
static from(cmd, enumValue) {
const mapped = this.valueToOptionMap.find(it=>enumValue instanceof it.value)?.option ?? this;
const mapped = this.valueToOptionMap.find(it => enumValue instanceof it.value)?.option ?? this;
return new mapped(cmd, enumValue);
}
/**@type {{value:(typeof SlashCommandEnumValue), option:(typeof SlashCommandEnumAutoCompleteOption)}[]} */
@@ -18,7 +18,6 @@ export class SlashCommandEnumAutoCompleteOption extends AutoCompleteOption {
/**@type {SlashCommandEnumValue}*/ enumValue;
/**
* @param {SlashCommand} cmd
* @param {SlashCommandEnumValue} enumValue
@@ -48,7 +48,6 @@ export class SlashCommandExecutionError extends Error {
}
constructor(cause, message, commandName, start, end, commandText, fullText) {
super(message, { cause });
this.commandName = commandName;
@@ -17,10 +17,10 @@ export class SlashCommandExecutor {
get source() { return this.#source; }
set source(value) {
this.#source = value;
for (const arg of this.namedArgumentList.filter(it=>it.value instanceof SlashCommandClosure)) {
for (const arg of this.namedArgumentList.filter(it => it.value instanceof SlashCommandClosure)) {
arg.value.source = value;
}
for (const arg of this.unnamedArgumentList.filter(it=>it.value instanceof SlashCommandClosure)) {
for (const arg of this.unnamedArgumentList.filter(it => it.value instanceof SlashCommandClosure)) {
arg.value.source = value;
}
}
@@ -31,15 +31,15 @@ export class SlashCommandExecutor {
get commandCount() {
return 1
+ this.namedArgumentList.filter(it=>it.value instanceof SlashCommandClosure).map(it=>/**@type {SlashCommandClosure}*/(it.value).commandCount).reduce((cur, sum)=>cur + sum, 0)
+ this.unnamedArgumentList.filter(it=>it.value instanceof SlashCommandClosure).map(it=>/**@type {SlashCommandClosure}*/(it.value).commandCount).reduce((cur, sum)=>cur + sum, 0)
+ this.namedArgumentList.filter(it => it.value instanceof SlashCommandClosure).map(it =>/**@type {SlashCommandClosure}*/(it.value).commandCount).reduce((cur, sum) => cur + sum, 0)
+ this.unnamedArgumentList.filter(it => it.value instanceof SlashCommandClosure).map(it =>/**@type {SlashCommandClosure}*/(it.value).commandCount).reduce((cur, sum) => cur + sum, 0)
;
}
set onProgress(value) {
const closures = /**@type {SlashCommandClosure[]}*/([
...this.namedArgumentList.filter(it=>it.value instanceof SlashCommandClosure).map(it=>it.value),
...this.unnamedArgumentList.filter(it=>it.value instanceof SlashCommandClosure).map(it=>it.value),
...this.namedArgumentList.filter(it => it.value instanceof SlashCommandClosure).map(it => it.value),
...this.unnamedArgumentList.filter(it => it.value instanceof SlashCommandClosure).map(it => it.value),
]);
for (const closure of closures) {
closure.onProgress = value;
@@ -65,7 +65,7 @@ export class SlashCommandParser {
static addCommandObject(command) {
const reserved = ['/', '#', ':', 'parser-flag', 'breakpoint'];
for (const start of reserved) {
if (command.name.toLowerCase().startsWith(start) || (command.aliases ?? []).find(a=>a.toLowerCase().startsWith(start))) {
if (command.name.toLowerCase().startsWith(start) || (command.aliases ?? []).find(a => a.toLowerCase().startsWith(start))) {
throw new Error(`Illegal Name. Slash command name cannot begin with "${start}".`);
}
}
@@ -80,15 +80,15 @@ export class SlashCommandParser {
console.trace('WARN: Duplicate slash command registered!', [command.name, ...command.aliases]);
}
const stack = new Error().stack.split('\n').map(it=>it.trim());
command.isExtension = stack.find(it=>it.includes('/scripts/extensions/')) != null;
command.isThirdParty = stack.find(it=>it.includes('/scripts/extensions/third-party/')) != null;
const stack = new Error().stack.split('\n').map(it => it.trim());
command.isExtension = stack.find(it => it.includes('/scripts/extensions/')) != null;
command.isThirdParty = stack.find(it => it.includes('/scripts/extensions/third-party/')) != null;
if (command.isThirdParty) {
command.source = stack.find(it=>it.includes('/scripts/extensions/third-party/')).replace(/^.*?\/scripts\/extensions\/third-party\/([^/]+)\/.*$/, '$1');
command.source = stack.find(it => it.includes('/scripts/extensions/third-party/')).replace(/^.*?\/scripts\/extensions\/third-party\/([^/]+)\/.*$/, '$1');
} else if (command.isExtension) {
command.source = stack.find(it=>it.includes('/scripts/extensions/')).replace(/^.*?\/scripts\/extensions\/([^/]+)\/.*$/, '$1');
command.source = stack.find(it => it.includes('/scripts/extensions/')).replace(/^.*?\/scripts\/extensions\/([^/]+)\/.*$/, '$1');
} else {
const idx = stack.findLastIndex(it=>it.includes('at SlashCommandParser.')) + 1;
const idx = stack.findLastIndex(it => it.includes('at SlashCommandParser.')) + 1;
command.source = stack[idx].replace(/^.*?\/((?:scripts\/)?(?:[^/]+)\.js).*$/, '$1');
}
@@ -153,7 +153,7 @@ export class SlashCommandParser {
description: 'The parser flag to modify.',
typeList: [ARGUMENT_TYPE.STRING],
isRequired: true,
enumList: Object.keys(PARSER_FLAG).map(flag=>new SlashCommandEnumValue(flag, help[PARSER_FLAG[flag]])),
enumList: Object.keys(PARSER_FLAG).map(flag => new SlashCommandEnumValue(flag, help[PARSER_FLAG[flag]])),
}),
SlashCommandArgument.fromProps({
description: 'The state of the parser flag to set.',
@@ -439,7 +439,7 @@ export class SlashCommandParser {
PIPEBREAK,
PIPE,
);
hljs.registerLanguage('stscript', ()=>({
hljs.registerLanguage('stscript', () => ({
case_insensitive: false,
keywords: [],
contains: [
@@ -480,19 +480,19 @@ export class SlashCommandParser {
}
}
const executor = this.commandIndex
.filter(it=>it.start <= index && (it.end >= index || it.end == null))
.filter(it => it.start <= index && (it.end >= index || it.end == null))
.slice(-1)[0]
?? null
;
if (executor) {
const childClosure = this.closureIndex
.find(it=>it.start <= index && (it.end >= index || it.end == null) && it.start > executor.start)
.find(it => it.start <= index && (it.end >= index || it.end == null) && it.start > executor.start)
?? null
;
if (childClosure !== null) return null;
// Check if cursor is inside a macro
const macroEntry = this.macroIndex.findLast(it=>it.start <= index && it.end >= index);
const macroEntry = this.macroIndex.findLast(it => it.start <= index && it.end >= index);
if (macroEntry) {
// Build macro info object for shared function
const macroContent = text.slice(macroEntry.start + 2, macroEntry.end - (text.slice(macroEntry.end - 2, macroEntry.end) === '}}' ? 2 : 0));
@@ -522,16 +522,16 @@ export class SlashCommandParser {
if (executor.name == ':') {
const options = this.scopeIndex[this.commandIndex.indexOf(executor)]
?.allVariableNames
?.map(it=>new SlashCommandVariableAutoCompleteOption(it))
?.map(it => new SlashCommandVariableAutoCompleteOption(it))
?? []
;
try {
if ('quickReplyApi' in globalThis) {
const qrApi = globalThis.quickReplyApi;
options.push(...qrApi.listSets()
.map(set=>qrApi.listQuickReplies(set).map(qr=>`${set}.${qr}`))
.map(set => qrApi.listQuickReplies(set).map(qr => `${set}.${qr}`))
.flat()
.map(qr=>new SlashCommandQuickReplyAutoCompleteOption(qr)),
.map(qr => new SlashCommandQuickReplyAutoCompleteOption(qr)),
);
}
} catch { /* empty */ }
@@ -540,8 +540,8 @@ export class SlashCommandParser {
executor.start,
options,
true,
()=>`No matching variables in scope and no matching Quick Replies for "${result.name}"`,
()=>'No variables in scope and no Quick Replies found.',
() => `No matching variables in scope and no matching Quick Replies for "${result.name}"`,
() => 'No variables in scope and no Quick Replies found.',
);
return result;
}
@@ -741,7 +741,7 @@ export class SlashCommandParser {
return this.testSymbol(':}');
}
parseClosure(isRoot = false) {
const closureIndexEntry = { start:this.index + 1, end:null };
const closureIndexEntry = { start: this.index + 1, end: null };
this.closureIndex.push(closureIndexEntry);
let injectPipe = true;
if (!isRoot) this.take(2); // discard opening {:
@@ -1023,14 +1023,14 @@ export class SlashCommandParser {
cmd.unnamedArgumentList = this.parseUnnamedArgument(cmd.command?.unnamedArgumentList?.length && cmd?.command?.splitUnnamedArgument, cmd?.command?.splitUnnamedArgumentCount, rawQuotes);
cmd.endUnnamedArgs = this.index;
if (cmd.name == 'let') {
const keyArg = cmd.namedArgumentList.find(it=>it.name == 'key');
const keyArg = cmd.namedArgumentList.find(it => it.name == 'key');
if (keyArg) {
this.scope.variableNames.push(keyArg.value.toString());
} else if (typeof cmd.unnamedArgumentList[0]?.value == 'string') {
this.scope.variableNames.push(cmd.unnamedArgumentList[0].value);
}
} else if (cmd.name == 'import') {
const value = /**@type {string[]}*/(cmd.unnamedArgumentList.map(it=>it.value));
const value = /**@type {string[]}*/(cmd.unnamedArgumentList.map(it => it.value));
for (let i = 0; i < value.length; i++) {
const srcName = value[i];
let dstName = srcName;
@@ -5,7 +5,7 @@ export class SlashCommandScope {
/** @type {string[]} */ variableNames = [];
get allVariableNames() {
const names = [...this.variableNames, ...(this.parent?.allVariableNames ?? [])];
return names.filter((it,idx)=>idx == names.indexOf(it));
return names.filter((it, idx) => idx == names.indexOf(it));
}
// @ts-ignore
/** @type {object.<string, string|SlashCommandClosure>} */ variables = {};
@@ -13,7 +13,7 @@ export class SlashCommandScope {
/** @type {object.<string, string|SlashCommandClosure>} */ macros = {};
/** @type {{key:string, value:string|SlashCommandClosure}[]} */
get macroList() {
return [...Object.keys(this.macros).map(key=>({ key, value:this.macros[key] })), ...(this.parent?.macroList ?? [])];
return [...Object.keys(this.macros).map(key => ({ key, value: this.macros[key] })), ...(this.parent?.macroList ?? [])];
}
/** @type {SlashCommandScope} */ parent;
/** @type {string} */ #pipe;
@@ -40,7 +40,7 @@ export class SlashCommandScope {
setMacro(key, value, overwrite = true) {
if (overwrite || !this.macroList.find(it=>it.key == key)) {
if (overwrite || !this.macroList.find(it => it.key == key)) {
this.macros[key] = value;
}
}
@@ -109,8 +109,6 @@ export class SlashCommandScope {
}
export class SlashCommandScopeVariableExistsError extends Error {}
-1
View File
@@ -2091,7 +2091,6 @@ function onTagAsFolderClick() {
// If folder display has changed, we have to redraw the character list, otherwise this folders state would not change
printCharactersDebounced();
saveSettingsDebounced();
}
function updateDrawTagFolder(element, tag) {
+2 -3
View File
@@ -802,7 +802,6 @@ async function getStatusTextgen() {
console.info('Status check aborted.', err.reason);
} else {
console.error('Error getting status', err);
}
setOnlineStatus('no_connection');
}
@@ -1126,7 +1125,7 @@ export function initTextGenSettings() {
* @returns void
*/
function showSamplerControls(apiType = null) {
$('#textgenerationwebui_api-settings [data-tg-samplers], #textgenerationwebui_api [data-tg-samplers]').each(function(idx, elem) {
$('#textgenerationwebui_api-settings [data-tg-samplers], #textgenerationwebui_api [data-tg-samplers]').each(function (idx, elem) {
const typeSpecificControlled = $(elem).data('tg-type') !== undefined;
if (!typeSpecificControlled) $(this).show();
@@ -1139,7 +1138,7 @@ function showSamplerControls(apiType = null) {
if (!samplersActivatedManually?.length || !prioritizeManualSamplerSelect) return;
$('#textgenerationwebui_api-settings [data-tg-samplers], #textgenerationwebui_api [data-tg-samplers]').each(function() {
$('#textgenerationwebui_api-settings [data-tg-samplers], #textgenerationwebui_api [data-tg-samplers]').each(function () {
const tgSamplers = $(this).attr('data-tg-samplers').split(',').map(x => x.trim()).filter(str => str !== '');
for (const tgSampler of tgSamplers) {
-4
View File
@@ -455,7 +455,6 @@ async function changeName(handle, name, callback) {
toastr.success('Name changed successfully', 'Name Changed');
callback();
} catch (error) {
console.error('Error changing name:', error);
}
@@ -495,7 +494,6 @@ async function restoreSnapshot(name, callback) {
} catch (error) {
console.error('Error restoring snapshot:', error);
}
}
/**
@@ -601,7 +599,6 @@ async function viewSettingsSnapshots() {
const content = await loadSnapshotContent(snapshot.name);
contentBlock.val(content);
}
});
template.find('.snapshotList').append(snapshotBlock);
}
@@ -667,7 +664,6 @@ async function resetEverything(callback) {
} catch (error) {
console.error('Error resetting everything:', error);
}
}
async function openUserProfile() {
-1
View File
@@ -2310,7 +2310,6 @@ export function highlightRegex(regexStr) {
flags: new RegExp('(?<=\\/)([gimsuy]*)$', 'g'), // Match trailing flags
delimiters: new RegExp('^\\/|(?<![\\\\<])\\/', 'g'), // Match leading or trailing delimiters
};
} catch (error) {
return {
brackets: new RegExp('(\\\\)?\\[.*?\\]', 'g'), // Non-escaped square brackets
-2
View File
@@ -675,7 +675,6 @@ class WorldInfoTimedEffects {
console.log('[WI] Timed effect "delay" applied to entry', entry);
}
}
}
/**
@@ -4495,7 +4494,6 @@ function parseDecorators(content) {
}
return [[], content];
}
/**
-2
View File
@@ -111,7 +111,6 @@ router.post('/get', async (request, response) => {
try {
if (fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory()) {
ensureFoldersExist(request.user.directories);
const folders = fs.readdirSync(folderPath, { withFileTypes: true })
@@ -346,7 +345,6 @@ router.post('/character', async (request, response) => {
let output = [];
try {
if (fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory()) {
// Live2d assets
if (category == 'live2d') {
const folders = fs.readdirSync(folderPath, { withFileTypes: true });
@@ -541,7 +541,6 @@ llamacpp.post('/props', async function (request, response) {
console.debug('LlamaCpp props response:', data);
return response.send(data);
} catch (error) {
console.error(error);
return response.sendStatus(500);
@@ -591,7 +590,6 @@ llamacpp.post('/slots', async function (request, response) {
console.debug('LlamaCpp slots response:', data);
return response.send(data);
} catch (error) {
console.error(error);
return response.sendStatus(500);
+1 -1
View File
@@ -11,7 +11,7 @@ router.post('/chat/get', async (request, response) => {
const backupModels = [];
const backupFiles = await fsPromises
.readdir(request.user.directories.backups, { withFileTypes: true })
.then(d => d .filter(d => d.isFile() && path.extname(d.name) === '.jsonl' && d.name.startsWith(CHAT_BACKUPS_PREFIX)).map(d => d.name));
.then(d => d.filter(d => d.isFile() && path.extname(d.name) === '.jsonl' && d.name.startsWith(CHAT_BACKUPS_PREFIX)).map(d => d.name));
for (const name of backupFiles) {
const filePath = path.join(request.user.directories.backups, name);
+2 -2
View File
@@ -639,7 +639,6 @@ function charaFormatData(data, directories) {
if (file && file.entries) {
_.set(char, 'data.character_book', convertWorldInfoToCharacterBook(data.world, file.entries));
}
} catch {
console.warn(`Failed to read world info file: ${data.world}. Character book will not be available.`);
}
@@ -921,7 +920,8 @@ async function importFromJson(uploadPath, { request }, preservedFileName) {
let charJSON = JSON.stringify(char);
const result = await writeCharacterData(DEFAULT_AVATAR_PATH, charJSON, pngName, request);
return result ? pngName : '';
} else if (jsonData.char_name !== undefined) {//json Pygmalion notepad
} else if (jsonData.char_name !== undefined) {
//json Pygmalion notepad
console.info('Importing from gradio json');
jsonData.char_name = sanitize(jsonData.char_name);
if (jsonData.creator_notes) {
-2
View File
@@ -367,7 +367,6 @@ router.post('/version', async (request, response) => {
const { isUpToDate, remoteUrl } = await checkIfRepoIsUpToDate(extensionPath);
return response.send({ currentBranchName, currentCommitHash, isUpToDate, remoteUrl });
} catch (error) {
console.error('Getting extension version failed', error);
return response.status(500).send(`Server Error: ${error.message}`);
@@ -406,7 +405,6 @@ router.post('/delete', async (request, response) => {
console.info(`Extension has been deleted at ${extensionPath}`);
return response.send(`Extension has been deleted at ${extensionPath}`);
} catch (error) {
console.error('Deleting custom content failed', error);
return response.status(500).send(`Server Error: ${error.message}`);
+1 -1
View File
@@ -51,7 +51,7 @@ export async function migrateGroupChatsMetadataFormat(userDirectories) {
if (!needsMigration) {
continue;
}
if (!fs.existsSync(backupPath)){
if (!fs.existsSync(backupPath)) {
await fsPromises.mkdir(backupPath, { recursive: true });
}
await fsPromises.copyFile(groupFilePath, path.join(backupPath, groupFile.name));
-2
View File
@@ -256,7 +256,6 @@ router.post('/caption-image', async (request, response) => {
console.info(status);
if (status.state === HordeAsyncRequestStates.done) {
if (status.forms === undefined) {
console.error('Image interrogation request failed: no forms found.');
return response.sendStatus(500);
@@ -278,7 +277,6 @@ router.post('/caption-image', async (request, response) => {
return response.sendStatus(503);
}
}
} catch (error) {
console.error(error);
response.sendStatus(500);
-1
View File
@@ -442,7 +442,6 @@ router.post('/', async function (request, response) {
}
return response.status(400).json({ error: 'Invalid request format.' });
} catch (error) {
console.error('[ImageMetadata] API error:', error);
return response.status(500).json({ error: 'Internal server error.' });
-2
View File
@@ -189,7 +189,6 @@ router.post('/generate-voice', async (request, response) => {
response.setHeader('Content-Length', audioBytes.length);
return response.send(Buffer.from(audioBytes));
} catch (conversionError) {
console.error('MiniMax TTS: Audio conversion error:', conversionError);
return response.status(500).json({ error: `Audio data conversion failed: ${conversionError.message}` });
@@ -222,7 +221,6 @@ router.post('/generate-voice', async (request, response) => {
console.error('MiniMax TTS: No valid audio data in response:', responseData);
return response.status(500).json({ error: `API Error: ${errorMessage}` });
}
} catch (error) {
console.error('MiniMax TTS generation failed:', error);
return response.status(500).json({ error: 'Internal server error' });
-1
View File
@@ -158,7 +158,6 @@ router.post('/samplers', async (request, response) => {
const data = await result.json();
const names = data.map(x => x.name);
return response.send(names);
} catch (error) {
console.error(error);
return response.sendStatus(500);
-1
View File
@@ -301,7 +301,6 @@ publicRouter.get('/', async function (request, response) {
// Send a 404 so the frontend can display a placeholder
return response.sendStatus(404);
} catch (error) {
console.error('Failed getting thumbnail', error);
return response.sendStatus(500);
+1 -2
View File
@@ -118,7 +118,6 @@ export function postProcessPrompt(messages, type, names) {
* @copyright Prompt Conversion script taken from RisuAI by kwaroran (GPLv3).
*/
export function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill, withSysPromptSupport, useSystemPrompt, addSysHumanMsg, excludePrefixes) {
//Prepare messages for claude.
//When 'Exclude Human/Assistant prefixes' checked, setting messages role to the 'system'(last message is exception).
if (messages.length > 0) {
@@ -1278,7 +1277,7 @@ export function calculateGoogleBudgetTokens(maxTokens, reasoningEffort, model) {
return getGemini3ProBudget();
}
if (/gemini-3-flash/.test(model) ) {
if (/gemini-3-flash/.test(model)) {
return getGemini3FlashBudget();
}
-1
View File
@@ -1012,7 +1012,6 @@ export async function canResolve(name, useIPv6 = true, useIPv4 = true) {
}
return v6Resolved || v4Resolved;
} catch (error) {
return false;
}