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