Chore: Add code formatting conventions as eslint rules (#5158) * Add code formatting conventions as eslint rules * Improve formatting in addQuickReply

357da3219b6686616c3435524fa23ac987eff840

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
94 files changed, +366 -566Ignore whitespace
.eslintrc.cjs+23 -0
@@ -102,5 +102,28 @@ module.exports = {
102 // These rules should eventually be enabled.102 // These rules should eventually be enabled.
103 'no-async-promise-executor': 'off',103 'no-async-promise-executor': 'off',
104 'no-inner-declarations': 'off',104 'no-inner-declarations': 'off',
105 'brace-style': 'off',
106 // Additional formatting rules based on codebase conventions
107 'array-bracket-spacing': ['error', 'never'],
108 'computed-property-spacing': ['error', 'never'],
109 'block-spacing': ['error', 'always'],
110 'keyword-spacing': ['error', { before: true, after: true }],
111 'space-before-blocks': ['error', 'always'],
112 'space-before-function-paren': ['error', { anonymous: 'always', named: 'never', asyncArrow: 'always' }],
113 'space-in-parens': ['error', 'never'],
114 'comma-spacing': ['error', { before: false, after: true }],
115 'key-spacing': ['error', { beforeColon: false, afterColon: true }],
116 'func-call-spacing': ['error', 'never'],
117 'no-multiple-empty-lines': ['error', { max: 2, maxEOF: 1, maxBOF: 0 }],
118 'padded-blocks': ['error', 'never'],
119 'no-whitespace-before-property': 'error',
120 'space-unary-ops': ['error', { words: true, nonwords: false }],
121 'arrow-spacing': ['error', { before: true, after: true }],
122 'template-curly-spacing': ['error', 'never'],
123 'rest-spread-spacing': ['error', 'never'],
124 'generator-star-spacing': ['error', { before: false, after: true }],
125 'yield-star-spacing': ['error', { before: false, after: true }],
126 'template-tag-spacing': ['error', 'never'],
127 'switch-colon-spacing': ['error', { after: true, before: false }],
105 },128 },
106};129};
public/script.js+2 -14
@@ -2516,7 +2516,6 @@ export function addOneMessage(mes, { type = undefined, insertAfter = null, scrol
2516 * @returns {JQuery<HTMLElement>} Rendered HTMLElement.2516 * @returns {JQuery<HTMLElement>} Rendered HTMLElement.
2517 */2517 */
2518export function updateMessageElement(mes, { messageId = chat.length - 1, messageElement = messageTemplate.clone(), adjustMediaScroll = SCROLL_BEHAVIOR.NONE } = {}) {2518export function updateMessageElement(mes, { messageId = chat.length - 1, messageElement = messageTemplate.clone(), adjustMediaScroll = SCROLL_BEHAVIOR.NONE } = {}) {
2519
2520 let avatarImg = getThumbnailUrl('persona', user_avatar);2519 let avatarImg = getThumbnailUrl('persona', user_avatar);
25212520
2522 //for non-user messages2521 //for non-user messages
@@ -3709,9 +3708,9 @@ class StreamingProcessor {
3709 }3708 }
37103709
3711 /**3710 /**
3712 * @returns {Generator<{ text: string, swipes: string[], logprobs: import('./scripts/logprobs.js').TokenLogprobs, toolCalls: any[], state: any }, void, void>}3711 * @returns {AsyncGenerator<{ text: string, swipes: string[], logprobs: import('./scripts/logprobs.js').TokenLogprobs, toolCalls: any[], state: any }, void, void>}
3713 */3712 */
3714 *nullStreamingGeneration() {3713 async* nullStreamingGeneration() {
3715 throw new Error('Generation function for streaming is not hooked up');3714 throw new Error('Generation function for streaming is not hooked up');
3716 }3715 }
37173716
@@ -4862,7 +4861,6 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
48624861
4863 // Add quiet generation prompt at depth 04862 // Add quiet generation prompt at depth 0
4864 if (quiet_prompt && quiet_prompt.length) {4863 if (quiet_prompt && quiet_prompt.length) {
4865
4866 // here name1 is forced for all quiet prompts..why?4864 // here name1 is forced for all quiet prompts..why?
4867 const name = name1;4865 const name = name1;
4868 //checks if we are in instruct, if so, formats the chat as such, otherwise just adds the quiet prompt4866 //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
5399 if (!isAborted && power_user.auto_swipe && generatedTextFiltered(getMessage)) {5397 if (!isAborted && power_user.auto_swipe && generatedTextFiltered(getMessage)) {
5400 is_send_press = false;5398 is_send_press = false;
5401 return await swipe(null, SWIPE_DIRECTION.RIGHT, { source: SWIPE_SOURCE.AUTO_SWIPE, repeated: true, forceMesId: chat.length - 1 });5399 return await swipe(null, SWIPE_DIRECTION.RIGHT, { source: SWIPE_SOURCE.AUTO_SWIPE, repeated: true, forceMesId: chat.length - 1 });
5402
5403 }5400 }
54045401
5405 console.debug('/api/chats/save called by /Generate');5402 console.debug('/api/chats/save called by /Generate');
@@ -6517,7 +6514,6 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title
6517 !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type);6514 !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type);
6518 addOneMessage(chat[chat_id], { type: 'swipe' });6515 addOneMessage(chat[chat_id], { type: 'swipe' });
6519 !fromStreaming && await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, chat_id, type);6516 !fromStreaming && await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, chat_id, type);
6520
6521 } else {6517 } else {
6522 console.debug('entering chat update routine for non-swipe post');6518 console.debug('entering chat update routine for non-swipe post');
6523 const newMessage = {};6519 const newMessage = {};
@@ -8247,7 +8243,6 @@ export async function getChatsFromFiles(data, isGroupChat) {
8247 currentChat.shift();8243 currentChat.shift();
8248 }8244 }
8249 chat_dict[file_name] = currentChat;8245 chat_dict[file_name] = currentChat;
8250
8251 } catch (error) {8246 } catch (error) {
8252 console.error(error);8247 console.error(error);
8253 }8248 }
@@ -9588,7 +9583,6 @@ export async function createOrEditCharacter(e) {
9588 select_rm_info('char_create', avatarId, oldSelectedChar);9583 select_rm_info('char_create', avatarId, oldSelectedChar);
95899584
9590 crop_data = undefined;9585 crop_data = undefined;
9591
9592 } catch (error) {9586 } catch (error) {
9593 console.error('Error creating character', error);9587 console.error('Error creating character', error);
9594 toastr.error(t`Failed to create character`);9588 toastr.error(t`Failed to create character`);
@@ -9984,7 +9978,6 @@ export async function swipe(event, direction, { source, repeated, message = chat
9984 duration: 0, //used to be 100 //Disabled on Cohee's request. https://github.com/SillyTavern/SillyTavern/pull/4610/files#r24087317449978 duration: 0, //used to be 100 //Disabled on Cohee's request. https://github.com/SillyTavern/SillyTavern/pull/4610/files#r2408731744
9985 queue: false,9979 queue: false,
9986 progress: function (animation, progress, remainingMs) {9980 progress: function (animation, progress, remainingMs) {
9987
9988 if (is_animation_scroll) chatElement.scrollTop(getMessageBottomHeight(thisMesDiv));9981 if (is_animation_scroll) chatElement.scrollTop(getMessageBottomHeight(thisMesDiv));
9989 },9982 },
9990 complete: function () {9983 complete: function () {
@@ -10001,7 +9994,6 @@ export async function swipe(event, direction, { source, repeated, message = chat
10001 * @param {boolean} [skipSwipeOut=false]9994 * @param {boolean} [skipSwipeOut=false]
10002 */9995 */
10003 async function animateSwipe(run_generate = false, skipSwipeOut = false) {9996 async function animateSwipe(run_generate = false, skipSwipeOut = false) {
10004
10005 if (!skipSwipeOut) {9997 if (!skipSwipeOut) {
10006 //Swipe out.9998 //Swipe out.
10007 await animateSwipeTransition(mesId, { xEnd: `${swipeRange}px`, duration: swipeDuration });9999 await animateSwipeTransition(mesId, { xEnd: `${swipeRange}px`, duration: swipeDuration });
@@ -10069,7 +10061,6 @@ export async function swipe(event, direction, { source, repeated, message = chat
1006910061
10070 //If the swipe is not being deleted.10062 //If the swipe is not being deleted.
10071 if (source != SWIPE_SOURCE.DELETE && source != SWIPE_SOURCE.BACK) {10063 if (source != SWIPE_SOURCE.DELETE && source != SWIPE_SOURCE.BACK) {
10072
10073 // Make sure ad-hoc changes to extras are saved before swiping away10064 // Make sure ad-hoc changes to extras are saved before swiping away
10074 syncMesToSwipe(mesId);10065 syncMesToSwipe(mesId);
1007510066
@@ -10382,7 +10373,6 @@ export async function doNewChat({ deleteCurrentChat = false } = {}) {
10382 await createOrEditCharacter(new CustomEvent('newChat'));10373 await createOrEditCharacter(new CustomEvent('newChat'));
10383 if (deleteCurrentChat) await delChat(chat_file_for_del + '.jsonl');10374 if (deleteCurrentChat) await delChat(chat_file_for_del + '.jsonl');
10384 }10375 }
10385
10386}10376}
1038710377
10388/**10378/**
@@ -11407,9 +11397,7 @@ jQuery(async function () {
1140711397
11408 divchat.style.borderRadius = '';11398 divchat.style.borderRadius = '';
11409 divchat.style.backgroundColor = '';11399 divchat.style.backgroundColor = '';
11410
11411 } else {11400 } else {
11412
11413 divchat.style.borderRadius = '10px'; // Adjust the value to control the roundness of the corners11401 divchat.style.borderRadius = '10px'; // Adjust the value to control the roundness of the corners
11414 divchat.style.backgroundColor = ''; // Set the background color to your preference11402 divchat.style.backgroundColor = ''; // Set the background color to your preference
1141511403
public/scripts/BulkEditOverlay.js+2 -2
@@ -101,7 +101,7 @@ class CharacterContextMenu {
101 * @param {number} characterId101 * @param {number} characterId
102 * @returns {Promise<void>}102 * @returns {Promise<void>}
103 */103 */
104 static persona = async (characterId) => void(await convertCharacterToPersona(characterId));104 static persona = async (characterId) => void (await convertCharacterToPersona(characterId));
105105
106 /**106 /**
107 * Delete one or more characters,107 * Delete one or more characters,
@@ -754,7 +754,7 @@ class BulkEditOverlay {
754754
755 handleContextMenuShow = (event) => {755 handleContextMenuShow = (event) => {
756 event.preventDefault();756 event.preventDefault();
757 const [x,y] = this.#getContextMenuPosition(event);757 const [x, y] = this.#getContextMenuPosition(event);
758 CharacterContextMenu.show(x, y);758 CharacterContextMenu.show(x, y);
759 this.#contextMenuOpen = true;759 this.#contextMenuOpen = true;
760 };760 };
public/scripts/PromptManager.js+0 -2
@@ -986,7 +986,6 @@ class PromptManager {
986 * @returns {void}986 * @returns {void}
987 */987 */
988 addPrompt(prompt, identifier) {988 addPrompt(prompt, identifier) {
989
990 if (typeof prompt !== 'object' || prompt === null) throw new Error('Object is not a prompt');989 if (typeof prompt !== 'object' || prompt === null) throw new Error('Object is not a prompt');
991990
992 const newPrompt = {991 const newPrompt = {
@@ -1318,7 +1317,6 @@ class PromptManager {
1318 this.updatePromptByIdentifier(identifier, prompt);1317 this.updatePromptByIdentifier(identifier, prompt);
1319 debouncedSaveServiceSettings().then(() => this.render());1318 debouncedSaveServiceSettings().then(() => this.render());
1320 });1319 });
1321
1322 }1320 }
13231321
1324 /**1322 /**
public/scripts/RossAscends-mods.js+0 -3
@@ -101,7 +101,6 @@ observer.observe(document.documentElement, observerConfig);
101 * @returns {string} - A human-readable string that represents the time spent generating characters.101 * @returns {string} - A human-readable string that represents the time spent generating characters.
102 */102 */
103export function humanizeGenTime(total_gen_time) {103export function humanizeGenTime(total_gen_time) {
104
105 //convert time_spent to humanized format of "_ Hours, _ Minutes, _ Seconds" from milliseconds104 //convert time_spent to humanized format of "_ Hours, _ Minutes, _ Seconds" from milliseconds
106 let time_spent = total_gen_time || 0;105 let time_spent = total_gen_time || 0;
107 time_spent = Math.floor(time_spent / 1000);106 time_spent = Math.floor(time_spent / 1000);
@@ -1274,8 +1273,6 @@ export function initRossMods() {
1274 }1273 }
12751274
12761275
1277
1278
1279 if (event.ctrlKey && /^[1-9]$/.test(event.key)) {1276 if (event.ctrlKey && /^[1-9]$/.test(event.key)) {
1280 // This will eventually be to trigger quick replies1277 // This will eventually be to trigger quick replies
1281 // event.preventDefault();1278 // event.preventDefault();
public/scripts/autocomplete/AutoComplete.js+0 -5
@@ -80,8 +80,6 @@ export class AutoComplete {
80 }80 }
8181
8282
83
84
85 /**83 /**
86 * @param {HTMLTextAreaElement|HTMLInputElement} textarea The textarea to receive autocomplete.84 * @param {HTMLTextAreaElement|HTMLInputElement} textarea The textarea to receive autocomplete.
87 * @param {() => boolean} checkIfActivate Function should return true only if under the current conditions, autocomplete should display (e.g., for slash commands: autoComplete.text[0] == '/')85 * @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 {
414 });412 });
415413
416414
417
418 if (this.isForceHidden) {415 if (this.isForceHidden) {
419 // hidden with escape416 // hidden with escape
420 return this.hide();417 return this.hide();
@@ -465,7 +462,6 @@ export class AutoComplete {
465 }462 }
466463
467464
468
469 /**465 /**
470 * Create updated DOM.466 * Create updated DOM.
471 */467 */
@@ -514,7 +510,6 @@ export class AutoComplete {
514 }510 }
515511
516512
517
518 /**513 /**
519 * Update position of DOM.514 * Update position of DOM.
520 */515 */
public/scripts/autocomplete/AutoCompleteFuzzyScore.js+0 -3
@@ -1,6 +1,3 @@
1
2
3
4export class AutoCompleteFuzzyScore {1export class AutoCompleteFuzzyScore {
5 /**@type {number}*/ start;2 /**@type {number}*/ start;
6 /**@type {number}*/ longestConsecutive;3 /**@type {number}*/ longestConsecutive;
public/scripts/autocomplete/AutoCompleteNameResult.js+0 -1
@@ -2,7 +2,6 @@ import { AutoCompleteNameResultBase } from './AutoCompleteNameResultBase.js';
2import { AutoCompleteSecondaryNameResult } from './AutoCompleteSecondaryNameResult.js';2import { AutoCompleteSecondaryNameResult } from './AutoCompleteSecondaryNameResult.js';
33
44
5
6export class AutoCompleteNameResult extends AutoCompleteNameResultBase {5export class AutoCompleteNameResult extends AutoCompleteNameResultBase {
7 /**6 /**
8 *7 *
public/scripts/autocomplete/AutoCompleteNameResultBase.js+2 -3
@@ -1,14 +1,13 @@
1import { AutoCompleteOption } from './AutoCompleteOption.js';1import { AutoCompleteOption } from './AutoCompleteOption.js';
22
33
4
5export class AutoCompleteNameResultBase {4export class AutoCompleteNameResultBase {
6 /**@type {string} */ name;5 /**@type {string} */ name;
7 /**@type {number} */ start;6 /**@type {number} */ start;
8 /**@type {AutoCompleteOption[]} */ optionList = [];7 /**@type {AutoCompleteOption[]} */ optionList = [];
9 /**@type {boolean} */ canBeQuoted = false;8 /**@type {boolean} */ canBeQuoted = false;
10 /**@type {()=>string} */ makeNoMatchText = ()=>`No matches found for "${this.name}"`;9 /**@type {()=>string} */ makeNoMatchText = () => `No matches found for "${this.name}"`;
11 /**@type {()=>string} */ makeNoOptionsText = ()=>'No options';10 /**@type {()=>string} */ makeNoOptionsText = () => 'No options';
1211
1312
14 /**13 /**
public/scripts/autocomplete/AutoCompleteOption.js+1 -2
@@ -1,7 +1,6 @@
1import { AutoCompleteFuzzyScore } from './AutoCompleteFuzzyScore.js';1import { AutoCompleteFuzzyScore } from './AutoCompleteFuzzyScore.js';
22
33
4
5export class AutoCompleteOption {4export class AutoCompleteOption {
6 /** @type {string} */ name;5 /** @type {string} */ name;
7 /** @type {string} */ typeIcon;6 /** @type {string} */ typeIcon;
@@ -72,7 +71,7 @@ export class AutoCompleteOption {
72 name.classList.add('name');71 name.classList.add('name');
73 name.classList.add('monospace');72 name.classList.add('monospace');
74 name.textContent = noSlash ? '' : '/';73 name.textContent = noSlash ? '' : '/';
75 key.split('').forEach(char=>{74 key.split('').forEach(char => {
76 const span = document.createElement('span'); {75 const span = document.createElement('span'); {
77 span.textContent = char;76 span.textContent = char;
78 name.append(span);77 name.append(span);
public/scripts/bulk-edit.js+1 -1
@@ -55,7 +55,7 @@ function onSelectAllButtonClick() {
5555
56 if (!atLeastOneSelected) {56 if (!atLeastOneSelected) {
57 // If none was selected, trigger click on all to deselect all of them57 // If none was selected, trigger click on all to deselect all of them
58 for(const character of characters) {58 for (const character of characters) {
59 const checked = $(character).find('.bulk_select_checkbox:checked') ?? false;59 const checked = $(character).find('.bulk_select_checkbox:checked') ?? false;
60 if (checked && character instanceof HTMLElement) {60 if (checked && character instanceof HTMLElement) {
61 characterGroupOverlay.toggleSingleCharacter(character);61 characterGroupOverlay.toggleSingleCharacter(character);
public/scripts/cfg-scale.js+0 -1
@@ -150,7 +150,6 @@ function onCfgMenuItemClick() {
150 setTimeout(function () {150 setTimeout(function () {
151 $('#cfgConfig').hide();151 $('#cfgConfig').hide();
152 }, animation_duration);152 }, animation_duration);
153
154 }153 }
155 //duplicate options menu close handler from script.js154 //duplicate options menu close handler from script.js
156 //because this listener takes priority155 //because this listener takes priority
public/scripts/chat-backups.js+1 -1
@@ -199,7 +199,7 @@ class BackupsBrowser {
199 const deleteButton = document.createElement('div');199 const deleteButton = document.createElement('div');
200 deleteButton.classList.add('right_menu_button', 'fa-solid', 'fa-trash');200 deleteButton.classList.add('right_menu_button', 'fa-solid', 'fa-trash');
201 deleteButton.title = t`Delete backup`;201 deleteButton.title = t`Delete backup`;
202 deleteButton.addEventListener('click',async () => {202 deleteButton.addEventListener('click', async () => {
203 const isDeleted = await this.deleteBackup(backup.file_name);203 const isDeleted = await this.deleteBackup(backup.file_name);
204 if (isDeleted) {204 if (isDeleted) {
205 listItem.remove();205 listItem.remove();
public/scripts/data-maid.js+0 -1
@@ -251,7 +251,6 @@ class DataMaidDialog {
251 categoryElement.remove();251 categoryElement.remove();
252 this.displayEmptyPlaceholder();252 this.displayEmptyPlaceholder();
253 });253 });
254
255 });254 });
256 categoryElement.querySelectorAll('.dataMaidItemDelete').forEach(button => {255 categoryElement.querySelectorAll('.dataMaidItemDelete').forEach(button => {
257 button.addEventListener('click', async () => {256 button.addEventListener('click', async () => {
public/scripts/extensions/assets/index.js+1 -2
@@ -94,8 +94,7 @@ async function downloadAssetsList(url) {
94 updateCurrentAssets().then(async function () {94 updateCurrentAssets().then(async function () {
95 fetch(url, { cache: 'no-cache' })95 fetch(url, { cache: 'no-cache' })
96 .then(response => response.json())96 .then(response => response.json())
97 .then(async function(json) {97 .then(async function (json) {
98
99 availableAssets = {};98 availableAssets = {};
100 $('#assets_menu').empty();99 $('#assets_menu').empty();
101100
public/scripts/extensions/expressions/index.js+1 -3
@@ -1414,7 +1414,6 @@ export async function getExpressionsList({ filterAvailable = false } = {}) {
1414 });1414 });
14151415
1416 if (apiResult.ok) {1416 if (apiResult.ok) {
1417
1418 const data = await apiResult.json();1417 const data = await apiResult.json();
1419 expressionsList = data.labels;1418 expressionsList = data.labels;
1420 return expressionsList;1419 return expressionsList;
@@ -1488,7 +1487,6 @@ function chooseSpriteForExpression(spriteFolderName, expression, { prevExpressio
1488 }1487 }
14891488
1490 return spriteFile;1489 return spriteFile;
1491
1492}1490}
14931491
1494/**1492/**
@@ -2333,7 +2331,7 @@ function migrateSettings() {
2333 name: 'expression-folder-override',2331 name: 'expression-folder-override',
2334 aliases: ['spriteoverride', 'costume'],2332 aliases: ['spriteoverride', 'costume'],
2335 callback: setSpriteFolderCommand,2333 callback: setSpriteFolderCommand,
2336 namedArgumentList:[2334 namedArgumentList: [
2337 SlashCommandNamedArgument.fromProps({2335 SlashCommandNamedArgument.fromProps({
2338 name: 'name',2336 name: 'name',
2339 description: 'Character name to set a subfolder for. If not provided, the character who last sent a message will be used.',2337 description: 'Character name to set a subfolder for. If not provided, the character who last sent a message will be used.',
public/scripts/extensions/gallery/index.js+0 -1
@@ -791,7 +791,6 @@ async function listGalleryCommand(args) {
791791
792 const items = await getGalleryItems(url);792 const items = await getGalleryItems(url);
793 return JSON.stringify(items.map(it => it.src));793 return JSON.stringify(items.map(it => it.src));
794
795 } catch (err) {794 } catch (err) {
796 console.error(err);795 console.error(err);
797 }796 }
public/scripts/extensions/quick-reply/api/QuickReplyApi.js+9 -15
@@ -10,22 +10,18 @@ export class QuickReplyApi {
10 /** @type {SettingsUi} */ settingsUi;10 /** @type {SettingsUi} */ settingsUi;
1111
1212
13
14
15 constructor(/** @type {QuickReplySettings} */settings, /** @type {SettingsUi} */settingsUi) {13 constructor(/** @type {QuickReplySettings} */settings, /** @type {SettingsUi} */settingsUi) {
16 this.settings = settings;14 this.settings = settings;
17 this.settingsUi = settingsUi;15 this.settingsUi = settingsUi;
18 }16 }
1917
2018
21
22
23 /**19 /**
24 * @param {QuickReply} qr20 * @param {QuickReply} qr
25 * @returns {QuickReplySet}21 * @returns {QuickReplySet}
26 */22 */
27 getSetByQr(qr) {23 getSetByQr(qr) {
28 return QuickReplySet.list.find(it=>it.qrList.includes(qr));24 return QuickReplySet.list.find(it => it.qrList.includes(qr));
29 }25 }
3026
31 /**27 /**
@@ -48,13 +44,11 @@ export class QuickReplyApi {
48 getQrByLabel(setName, label) {44 getQrByLabel(setName, label) {
49 const set = this.getSetByName(setName);45 const set = this.getSetByName(setName);
50 if (!set) return;46 if (!set) return;
51 if (Number.isInteger(label)) return set.qrList.find(it=>it.id == label);47 if (Number.isInteger(label)) return set.qrList.find(it => it.id == label);
52 return set.qrList.find(it=>it.label == label);48 return set.qrList.find(it => it.label == label);
53 }49 }
5450
5551
56
57
58 /**52 /**
59 * Executes a quick reply by its index and returns the result.53 * Executes a quick reply by its index and returns the result.
60 *54 *
@@ -63,7 +57,7 @@ export class QuickReplyApi {
63 */57 */
64 async executeQuickReplyByIndex(idx) {58 async executeQuickReplyByIndex(idx) {
65 const qr = [...this.settings.config.setList, ...(this.settings.chatConfig?.setList ?? [])]59 const qr = [...this.settings.config.setList, ...(this.settings.chatConfig?.setList ?? [])]
66 .map(it=>it.set.qrList)60 .map(it => it.set.qrList)
67 .flat()[idx]61 .flat()[idx]
68 ;62 ;
69 if (qr) {63 if (qr) {
@@ -400,7 +394,7 @@ export class QuickReplyApi {
400 if (oldSet) {394 if (oldSet) {
401 QuickReplySet.list.splice(QuickReplySet.list.indexOf(oldSet), 1, set);395 QuickReplySet.list.splice(QuickReplySet.list.indexOf(oldSet), 1, set);
402 } else {396 } else {
403 const idx = QuickReplySet.list.findIndex(it=>it.name.localeCompare(name) == 1);397 const idx = QuickReplySet.list.findIndex(it => it.name.localeCompare(name) == 1);
404 if (idx > -1) {398 if (idx > -1) {
405 QuickReplySet.list.splice(idx, 0, set);399 QuickReplySet.list.splice(idx, 0, set);
406 } else {400 } else {
@@ -460,7 +454,7 @@ export class QuickReplyApi {
460 * @returns array with the names of all quick reply sets454 * @returns array with the names of all quick reply sets
461 */455 */
462 listSets() {456 listSets() {
463 return QuickReplySet.list.map(it=>it.name);457 return QuickReplySet.list.map(it => it.name);
464 }458 }
465 /**459 /**
466 * Gets a list of all globally active quick reply sets.460 * Gets a list of all globally active quick reply sets.
@@ -468,7 +462,7 @@ export class QuickReplyApi {
468 * @returns array with the names of all quick reply sets462 * @returns array with the names of all quick reply sets
469 */463 */
470 listGlobalSets() {464 listGlobalSets() {
471 return this.settings.config.setList.map(it=>it.set.name);465 return this.settings.config.setList.map(it => it.set.name);
472 }466 }
473 /**467 /**
474 * Gets a list of all quick reply sets activated by the current chat.468 * Gets a list of all quick reply sets activated by the current chat.
@@ -476,7 +470,7 @@ export class QuickReplyApi {
476 * @returns array with the names of all quick reply sets470 * @returns array with the names of all quick reply sets
477 */471 */
478 listChatSets() {472 listChatSets() {
479 return this.settings.chatConfig?.setList?.flatMap(it=>it.set.name) ?? [];473 return this.settings.chatConfig?.setList?.flatMap(it => it.set.name) ?? [];
480 }474 }
481475
482 /**476 /**
@@ -490,7 +484,7 @@ export class QuickReplyApi {
490 if (!set) {484 if (!set) {
491 throw new Error(`No quick reply set with name "${name}" found.`);485 throw new Error(`No quick reply set with name "${name}" found.`);
492 }486 }
493 return set.qrList.map(it=>it.label);487 return set.qrList.map(it => it.label);
494 }488 }
495489
496 /**490 /**
public/scripts/extensions/quick-reply/index.js+13 -17
@@ -14,8 +14,6 @@ import { selected_group } from '../../group-chats.js';
14export { debounceAsync };14export { debounceAsync };
1515
1616
17
18
19const _VERBOSE = true;17const _VERBOSE = true;
20export const debug = (...msg) => _VERBOSE ? console.debug('[QR2]', ...msg) : null;18export const debug = (...msg) => _VERBOSE ? console.debug('[QR2]', ...msg) : null;
21export const log = (...msg) => _VERBOSE ? console.log('[QR2]', ...msg) : null;19export const log = (...msg) => _VERBOSE ? console.log('[QR2]', ...msg) : null;
@@ -54,8 +52,6 @@ let autoExec;
54export let quickReplyApi;52export let quickReplyApi;
5553
5654
57
58
59const loadSets = async () => {55const loadSets = async () => {
60 const response = await fetch('/api/settings/get', {56 const response = await fetch('/api/settings/get', {
61 method: 'POST',57 method: 'POST',
@@ -72,7 +68,7 @@ const loadSets = async () => {
72 set.disableSend = set.quickActionEnabled ?? false;68 set.disableSend = set.quickActionEnabled ?? false;
73 set.placeBeforeInput = set.placeBeforeInputEnabled ?? false;69 set.placeBeforeInput = set.placeBeforeInputEnabled ?? false;
74 set.injectInput = set.AutoInputInject ?? false;70 set.injectInput = set.AutoInputInject ?? false;
75 set.qrList = set.quickReplySlots.map((slot,idx)=>{71 set.qrList = set.quickReplySlots.map((slot, idx) => {
76 const qr = {};72 const qr = {};
77 qr.id = idx + 1;73 qr.id = idx + 1;
78 qr.label = slot.label ?? '';74 qr.label = slot.label ?? '';
@@ -87,7 +83,7 @@ const loadSets = async () => {
87 qr.executeOnNewChat = slot.autoExecute_newChat ?? false;83 qr.executeOnNewChat = slot.autoExecute_newChat ?? false;
88 qr.executeBeforeGeneration = slot.autoExecute_beforeGeneration ?? false;84 qr.executeBeforeGeneration = slot.autoExecute_beforeGeneration ?? false;
89 qr.automationId = slot.automationId ?? '';85 qr.automationId = slot.automationId ?? '';
90 qr.contextList = (slot.contextMenu ?? []).map(it=>({86 qr.contextList = (slot.contextMenu ?? []).map(it => ({
91 set: it.preset,87 set: it.preset,
92 isChained: it.chain,88 isChained: it.chain,
93 }));89 }));
@@ -99,8 +95,8 @@ const loadSets = async () => {
99 }95 }
100 }96 }
101 // need to load QR lists after all sets are loaded to be able to resolve context menu entries97 // need to load QR lists after all sets are loaded to be able to resolve context menu entries
102 setList.forEach((set, idx)=>{98 setList.forEach((set, idx) => {
103 QuickReplySet.list[idx].qrList = set.qrList.map(it=>QuickReply.from(it));99 QuickReplySet.list[idx].qrList = set.qrList.map(it => QuickReply.from(it));
104 QuickReplySet.list[idx].init();100 QuickReplySet.list[idx].init();
105 });101 });
106 log('sets: ', QuickReplySet.list);102 log('sets: ', QuickReplySet.list);
@@ -140,7 +136,7 @@ const executeIfReadyElseQueue = async (functionToCall, args) => {
140 await functionToCall(...args);136 await functionToCall(...args);
141 } else {137 } else {
142 log('queueing', { functionToCall, args });138 log('queueing', { functionToCall, args });
143 executeQueue.push(async()=>await functionToCall(...args));139 executeQueue.push(async () => await functionToCall(...args));
144 }140 }
145};141};
146142
@@ -183,9 +179,9 @@ const init = async () => {
183179
184 buttons = new ButtonUi(settings);180 buttons = new ButtonUi(settings);
185 buttons.show();181 buttons.show();
186 settings.onSave = ()=>buttons.refresh();182 settings.onSave = () => buttons.refresh();
187183
188 globalThis.executeQuickReplyByName = async(name, args = {}, options = {}) => {184 globalThis.executeQuickReplyByName = async (name, args = {}, options = {}) => {
189 let qr = [185 let qr = [
190 ...settings.config.setList,186 ...settings.config.setList,
191 ...(settings.chatConfig?.setList ?? []),187 ...(settings.chatConfig?.setList ?? []),
@@ -193,14 +189,14 @@ const init = async () => {
193 ]189 ]
194 .map(it => it.set.qrList)190 .map(it => it.set.qrList)
195 .flat()191 .flat()
196 .find(it=>it.label == name)192 .find(it => it.label == name)
197 ;193 ;
198 if (!qr) {194 if (!qr) {
199 let [setName, ...qrName] = name.split('.');195 let [setName, ...qrName] = name.split('.');
200 qrName = qrName.join('.');196 qrName = qrName.join('.');
201 let qrs = QuickReplySet.get(setName);197 let qrs = QuickReplySet.get(setName);
202 if (qrs) {198 if (qrs) {
203 qr = qrs.qrList.find(it=>it.label == qrName);199 qr = qrs.qrList.find(it => it.label == qrName);
204 }200 }
205 }201 }
206 if (qr && qr.onExecute) {202 if (qr && qr.onExecute) {
@@ -215,7 +211,7 @@ const init = async () => {
215 slash.init();211 slash.init();
216 autoExec = new AutoExecuteHandler(settings);212 autoExec = new AutoExecuteHandler(settings);
217213
218 eventSource.on(event_types.APP_READY, async()=>await finalizeInit());214 eventSource.on(event_types.APP_READY, async () => await finalizeInit());
219215
220 globalThis.quickReplyApi = quickReplyApi;216 globalThis.quickReplyApi = quickReplyApi;
221};217};
@@ -275,14 +271,14 @@ const onChatChanged = async (chatIdx) => {
275271
276 await autoExec.handleChatChanged();272 await autoExec.handleChatChanged();
277};273};
278eventSource.on(event_types.CHAT_CHANGED, (...args)=>executeIfReadyElseQueue(onChatChanged, args));274eventSource.on(event_types.CHAT_CHANGED, (...args) => executeIfReadyElseQueue(onChatChanged, args));
279eventSource.on(event_types.CHARACTER_DELETED, purgeCharacterQuickReplySets);275eventSource.on(event_types.CHARACTER_DELETED, purgeCharacterQuickReplySets);
280eventSource.on(event_types.CHARACTER_RENAMED, updateCharacterQuickReplySets);276eventSource.on(event_types.CHARACTER_RENAMED, updateCharacterQuickReplySets);
281277
282const onUserMessage = async () => {278const onUserMessage = async () => {
283 await autoExec.handleUser();279 await autoExec.handleUser();
284};280};
285eventSource.makeFirst(event_types.USER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onUserMessage, args));281eventSource.makeFirst(event_types.USER_MESSAGE_RENDERED, (...args) => executeIfReadyElseQueue(onUserMessage, args));
286282
287const onAiMessage = async (messageId) => {283const onAiMessage = async (messageId) => {
288 if (['...'].includes(chat[messageId]?.mes)) {284 if (['...'].includes(chat[messageId]?.mes)) {
@@ -292,7 +288,7 @@ const onAiMessage = async (messageId) => {
292288
293 await autoExec.handleAi();289 await autoExec.handleAi();
294};290};
295eventSource.makeFirst(event_types.CHARACTER_MESSAGE_RENDERED, (...args)=>executeIfReadyElseQueue(onAiMessage, args));291eventSource.makeFirst(event_types.CHARACTER_MESSAGE_RENDERED, (...args) => executeIfReadyElseQueue(onAiMessage, args));
296292
297const onGroupMemberDraft = async () => {293const onGroupMemberDraft = async () => {
298 await autoExec.handleGroupMemberDraft();294 await autoExec.handleGroupMemberDraft();
public/scripts/extensions/quick-reply/src/AutoExecuteHandler.js+1 -5
@@ -8,8 +8,6 @@ export class AutoExecuteHandler {
8 /** @type {Boolean[]}*/ preventAutoExecuteStack = [];8 /** @type {Boolean[]}*/ preventAutoExecuteStack = [];
99
1010
11
12
13 constructor(/** @type {QuickReplySettings} */settings) {11 constructor(/** @type {QuickReplySettings} */settings) {
14 this.settings = settings;12 this.settings = settings;
15 }13 }
@@ -20,13 +18,11 @@ export class AutoExecuteHandler {
20 }18 }
2119
2220
23
24
25 async performAutoExecute(/** @type {QuickReply[]} */qrList) {21 async performAutoExecute(/** @type {QuickReply[]} */qrList) {
26 for (const qr of qrList) {22 for (const qr of qrList) {
27 this.preventAutoExecuteStack.push(qr.preventAutoExecute);23 this.preventAutoExecuteStack.push(qr.preventAutoExecute);
28 try {24 try {
29 await qr.execute({ isAutoExecute:true });25 await qr.execute({ isAutoExecute: true });
30 } catch (ex) {26 } catch (ex) {
31 warn(ex);27 warn(ex);
32 } finally {28 } finally {
public/scripts/extensions/quick-reply/src/QuickReply.js+104 -114
@@ -22,13 +22,11 @@ export class QuickReply {
22 * @param {{ id?: number; contextList?: any; }} props22 * @param {{ id?: number; contextList?: any; }} props
23 */23 */
24 static from(props) {24 static from(props) {
25 props.contextList = (props.contextList ?? []).map((/** @type {any} */ it)=>QuickReplyContextLink.from(it));25 props.contextList = (props.contextList ?? []).map((/** @type {any} */ it) => QuickReplyContextLink.from(it));
26 return Object.assign(new this(), props);26 return Object.assign(new this(), props);
27 }27 }
2828
2929
30
31
32 /**@type {number}*/ id;30 /**@type {number}*/ id;
33 /**@type {string}*/ icon;31 /**@type {string}*/ icon;
34 /**@type {string}*/ label = '';32 /**@type {string}*/ label = '';
@@ -89,8 +87,6 @@ export class QuickReply {
89 }87 }
9088
9189
92
93
94 unrender() {90 unrender() {
95 this.dom?.remove();91 this.dom?.remove();
96 this.dom = null;92 this.dom = null;
@@ -129,7 +125,7 @@ export class QuickReply {
129 menu.show(evt);125 menu.show(evt);
130 }126 }
131 });127 });
132 root.addEventListener('click', (evt)=>{128 root.addEventListener('click', (evt) => {
133 if (evt.ctrlKey) {129 if (evt.ctrlKey) {
134 this.showEditor();130 this.showEditor();
135 return;131 return;
@@ -169,8 +165,6 @@ export class QuickReply {
169 }165 }
170166
171167
172
173
174 renderSettings(idx) {168 renderSettings(idx) {
175 if (!this.settingsDom) {169 if (!this.settingsDom) {
176 const item = document.createElement('div'); {170 const item = document.createElement('div'); {
@@ -190,7 +184,7 @@ export class QuickReply {
190 addNew.classList.add('fa-solid');184 addNew.classList.add('fa-solid');
191 addNew.classList.add('fa-plus');185 addNew.classList.add('fa-plus');
192 addNew.title = 'Add quick reply';186 addNew.title = 'Add quick reply';
193 addNew.addEventListener('click', ()=>this.onInsertBefore());187 addNew.addEventListener('click', () => this.onInsertBefore());
194 actions.append(addNew);188 actions.append(addNew);
195 }189 }
196 const paste = document.createElement('div'); {190 const paste = document.createElement('div'); {
@@ -201,7 +195,7 @@ export class QuickReply {
201 paste.classList.add('fa-solid');195 paste.classList.add('fa-solid');
202 paste.classList.add('fa-paste');196 paste.classList.add('fa-paste');
203 paste.title = 'Add quick reply from clipboard';197 paste.title = 'Add quick reply from clipboard';
204 paste.addEventListener('click', async()=>{198 paste.addEventListener('click', async () => {
205 const text = await navigator.clipboard.readText();199 const text = await navigator.clipboard.readText();
206 this.onInsertBefore(text);200 this.onInsertBefore(text);
207 });201 });
@@ -215,11 +209,11 @@ export class QuickReply {
215 importFile.classList.add('fa-solid');209 importFile.classList.add('fa-solid');
216 importFile.classList.add('fa-file-import');210 importFile.classList.add('fa-file-import');
217 importFile.title = 'Add quick reply from JSON file';211 importFile.title = 'Add quick reply from JSON file';
218 importFile.addEventListener('click', async()=>{212 importFile.addEventListener('click', async () => {
219 const inp = document.createElement('input'); {213 const inp = document.createElement('input'); {
220 inp.type = 'file';214 inp.type = 'file';
221 inp.accept = '.json';215 inp.accept = '.json';
222 inp.addEventListener('change', async()=>{216 inp.addEventListener('change', async () => {
223 if (inp.files.length > 0) {217 if (inp.files.length > 0) {
224 for (const file of inp.files) {218 for (const file of inp.files) {
225 const text = await file.text();219 const text = await file.text();
@@ -256,7 +250,7 @@ export class QuickReply {
256 icon.classList.add('fa-solid');250 icon.classList.add('fa-solid');
257 icon.classList.add(this.icon);251 icon.classList.add(this.icon);
258 }252 }
259 icon.addEventListener('click', async()=>{253 icon.addEventListener('click', async () => {
260 let value = await showFontAwesomePicker();254 let value = await showFontAwesomePicker();
261 this.updateIcon(value);255 this.updateIcon(value);
262 });256 });
@@ -267,7 +261,7 @@ export class QuickReply {
267 lbl.classList.add('qr--set-itemLabel');261 lbl.classList.add('qr--set-itemLabel');
268 lbl.classList.add('text_pole');262 lbl.classList.add('text_pole');
269 lbl.value = this.label;263 lbl.value = this.label;
270 lbl.addEventListener('input', ()=>this.updateLabel(lbl.value));264 lbl.addEventListener('input', () => this.updateLabel(lbl.value));
271 lblContainer.append(lbl);265 lblContainer.append(lbl);
272 }266 }
273 itemContent.append(lblContainer);267 itemContent.append(lblContainer);
@@ -283,7 +277,7 @@ export class QuickReply {
283 opt.classList.add('fa-solid');277 opt.classList.add('fa-solid');
284 opt.textContent = '⁝';278 opt.textContent = '⁝';
285 opt.title = 'Additional options:\n - large editor\n - context menu\n - auto-execution\n - tooltip';279 opt.title = 'Additional options:\n - large editor\n - context menu\n - auto-execution\n - tooltip';
286 opt.addEventListener('click', ()=>this.showEditor());280 opt.addEventListener('click', () => this.showEditor());
287 optContainer.append(opt);281 optContainer.append(opt);
288 }282 }
289 itemContent.append(optContainer);283 itemContent.append(optContainer);
@@ -294,7 +288,7 @@ export class QuickReply {
294 mes.classList.add('qr--set-itemMessage');288 mes.classList.add('qr--set-itemMessage');
295 mes.value = this.message;289 mes.value = this.message;
296 //HACK need to use jQuery to catch the triggered event from the expanded editor290 //HACK need to use jQuery to catch the triggered event from the expanded editor
297 $(mes).on('input', ()=>this.updateMessage(mes.value));291 $(mes).on('input', () => this.updateMessage(mes.value));
298 itemContent.append(mes);292 itemContent.append(mes);
299 }293 }
300 const actions = document.createElement('div'); {294 const actions = document.createElement('div'); {
@@ -306,7 +300,7 @@ export class QuickReply {
306 move.classList.add('fa-solid');300 move.classList.add('fa-solid');
307 move.classList.add('fa-truck-arrow-right');301 move.classList.add('fa-truck-arrow-right');
308 move.title = 'Move quick reply to other set';302 move.title = 'Move quick reply to other set';
309 move.addEventListener('click', ()=>this.onTransfer(this));303 move.addEventListener('click', () => this.onTransfer(this));
310 actions.append(move);304 actions.append(move);
311 }305 }
312 const copy = document.createElement('div'); {306 const copy = document.createElement('div'); {
@@ -316,7 +310,7 @@ export class QuickReply {
316 copy.classList.add('fa-solid');310 copy.classList.add('fa-solid');
317 copy.classList.add('fa-copy');311 copy.classList.add('fa-copy');
318 copy.title = 'Copy quick reply to clipboard';312 copy.title = 'Copy quick reply to clipboard';
319 copy.addEventListener('click', async()=>{313 copy.addEventListener('click', async () => {
320 await navigator.clipboard.writeText(JSON.stringify(this));314 await navigator.clipboard.writeText(JSON.stringify(this));
321 copy.classList.add('qr--success');315 copy.classList.add('qr--success');
322 await delay(3010);316 await delay(3010);
@@ -331,7 +325,7 @@ export class QuickReply {
331 cut.classList.add('fa-solid');325 cut.classList.add('fa-solid');
332 cut.classList.add('fa-cut');326 cut.classList.add('fa-cut');
333 cut.title = 'Cut quick reply to clipboard (copy and remove)';327 cut.title = 'Cut quick reply to clipboard (copy and remove)';
334 cut.addEventListener('click', async()=>{328 cut.addEventListener('click', async () => {
335 await navigator.clipboard.writeText(JSON.stringify(this));329 await navigator.clipboard.writeText(JSON.stringify(this));
336 this.delete();330 this.delete();
337 });331 });
@@ -344,8 +338,8 @@ export class QuickReply {
344 exp.classList.add('fa-solid');338 exp.classList.add('fa-solid');
345 exp.classList.add('fa-file-export');339 exp.classList.add('fa-file-export');
346 exp.title = 'Export quick reply as file';340 exp.title = 'Export quick reply as file';
347 exp.addEventListener('click', ()=>{341 exp.addEventListener('click', () => {
348 const blob = new Blob([JSON.stringify(this)], { type:'text' });342 const blob = new Blob([JSON.stringify(this)], { type: 'text' });
349 const url = URL.createObjectURL(blob);343 const url = URL.createObjectURL(blob);
350 const a = document.createElement('a'); {344 const a = document.createElement('a'); {
351 a.href = url;345 a.href = url;
@@ -363,7 +357,7 @@ export class QuickReply {
363 del.classList.add('fa-trash-can');357 del.classList.add('fa-trash-can');
364 del.classList.add('redWarningBG');358 del.classList.add('redWarningBG');
365 del.title = 'Remove Quick Reply\n---\nShift+Click to skip confirmation';359 del.title = 'Remove Quick Reply\n---\nShift+Click to skip confirmation';
366 del.addEventListener('click', async(evt)=>{360 del.addEventListener('click', async (evt) => {
367 if (!evt.shiftKey) {361 if (!evt.shiftKey) {
368 const result = await Popup.show.confirm(362 const result = await Popup.show.confirm(
369 'Remove Quick Reply',363 'Remove Quick Reply',
@@ -408,7 +402,7 @@ export class QuickReply {
408 else {402 else {
409 icon.textContent = '…';403 icon.textContent = '…';
410 }404 }
411 icon.addEventListener('click', async()=>{405 icon.addEventListener('click', async () => {
412 let value = await showFontAwesomePicker();406 let value = await showFontAwesomePicker();
413 if (value === null) return;407 if (value === null) return;
414 if (this.icon) icon.classList.remove(this.icon);408 if (this.icon) icon.classList.remove(this.icon);
@@ -425,18 +419,18 @@ export class QuickReply {
425 /**@type {HTMLInputElement}*/419 /**@type {HTMLInputElement}*/
426 const showLabel = dom.querySelector('#qr--modal-showLabel');420 const showLabel = dom.querySelector('#qr--modal-showLabel');
427 showLabel.checked = this.showLabel;421 showLabel.checked = this.showLabel;
428 showLabel.addEventListener('click', ()=>{422 showLabel.addEventListener('click', () => {
429 this.updateShowLabel(showLabel.checked);423 this.updateShowLabel(showLabel.checked);
430 });424 });
431 /**@type {HTMLInputElement}*/425 /**@type {HTMLInputElement}*/
432 const label = dom.querySelector('#qr--modal-label');426 const label = dom.querySelector('#qr--modal-label');
433 label.value = this.label;427 label.value = this.label;
434 label.addEventListener('input', ()=>{428 label.addEventListener('input', () => {
435 this.updateLabel(label.value);429 this.updateLabel(label.value);
436 });430 });
437 let switcherList;431 let switcherList;
438 // @ts-ignore432 // @ts-ignore
439 dom.querySelector('#qr--modal-switcher').addEventListener('click', (evt)=>{433 dom.querySelector('#qr--modal-switcher').addEventListener('click', (evt) => {
440 if (switcherList) {434 if (switcherList) {
441 switcherList.remove();435 switcherList.remove();
442 switcherList = null;436 switcherList = null;
@@ -445,15 +439,15 @@ export class QuickReply {
445 const list = document.createElement('ul'); {439 const list = document.createElement('ul'); {
446 switcherList = list;440 switcherList = list;
447 list.classList.add('qr--modal-switcherList');441 list.classList.add('qr--modal-switcherList');
448 const makeList = (qrs)=>{442 const makeList = (qrs) => {
449 const setItem = document.createElement('li'); {443 const setItem = document.createElement('li'); {
450 setItem.classList.add('qr--modal-switcherItem');444 setItem.classList.add('qr--modal-switcherItem');
451 setItem.addEventListener('click', ()=>{445 setItem.addEventListener('click', () => {
452 list.innerHTML = '';446 list.innerHTML = '';
453 for (const qrs of quickReplyApi.listSets()) {447 for (const qrs of quickReplyApi.listSets()) {
454 const item = document.createElement('li'); {448 const item = document.createElement('li'); {
455 item.classList.add('qr--modal-switcherItem');449 item.classList.add('qr--modal-switcherItem');
456 item.addEventListener('click', ()=>{450 item.addEventListener('click', () => {
457 list.innerHTML = '';451 list.innerHTML = '';
458 makeList(quickReplyApi.getSetByName(qrs));452 makeList(quickReplyApi.getSetByName(qrs));
459 });453 });
@@ -484,7 +478,7 @@ export class QuickReply {
484 }478 }
485 const addItem = document.createElement('li'); {479 const addItem = document.createElement('li'); {
486 addItem.classList.add('qr--modal-switcherItem');480 addItem.classList.add('qr--modal-switcherItem');
487 addItem.addEventListener('click', ()=>{481 addItem.addEventListener('click', () => {
488 const qr = quickReplyApi.getSetByQr(this).addQuickReply();482 const qr = quickReplyApi.getSetByQr(this).addQuickReply();
489 this.editorPopup.completeAffirmative();483 this.editorPopup.completeAffirmative();
490 qr.showEditor();484 qr.showEditor();
@@ -505,11 +499,11 @@ export class QuickReply {
505 }499 }
506 list.append(addItem);500 list.append(addItem);
507 }501 }
508 for (const qr of qrs.qrList.toSorted((a,b)=>a.label.toLowerCase().localeCompare(b.label.toLowerCase()))) {502 for (const qr of qrs.qrList.toSorted((a, b) => a.label.toLowerCase().localeCompare(b.label.toLowerCase()))) {
509 const item = document.createElement('li'); {503 const item = document.createElement('li'); {
510 item.classList.add('qr--modal-switcherItem');504 item.classList.add('qr--modal-switcherItem');
511 if (qr == this) item.classList.add('qr--current');505 if (qr == this) item.classList.add('qr--current');
512 else item.addEventListener('click', ()=>{506 else item.addEventListener('click', () => {
513 this.editorPopup.completeAffirmative();507 this.editorPopup.completeAffirmative();
514 qr.showEditor();508 qr.showEditor();
515 });509 });
@@ -588,7 +582,7 @@ export class QuickReply {
588 });582 });
589 };583 };
590 const updateScrollDebounced = updateScroll;584 const updateScrollDebounced = updateScroll;
591 const updateSyntaxEnabled = ()=>{585 const updateSyntaxEnabled = () => {
592 if (syntax.checked) {586 if (syntax.checked) {
593 dom.querySelector('#qr--modal-messageHolder').classList.remove('qr--noSyntax');587 dom.querySelector('#qr--modal-messageHolder').classList.remove('qr--noSyntax');
594 } else {588 } else {
@@ -623,7 +617,7 @@ export class QuickReply {
623 // @ts-ignore617 // @ts-ignore
624 if (navigator.keyboard) {618 if (navigator.keyboard) {
625 // @ts-ignore619 // @ts-ignore
626 navigator.keyboard.getLayoutMap().then(it=>dom.querySelector('#qr--modal-commentKey').textContent = it.get('Backslash'));620 navigator.keyboard.getLayoutMap().then(it => dom.querySelector('#qr--modal-commentKey').textContent = it.get('Backslash'));
627 } else {621 } else {
628 dom.querySelector('#qr--modal-commentKey').closest('small').remove();622 dom.querySelector('#qr--modal-commentKey').closest('small').remove();
629 }623 }
@@ -632,12 +626,12 @@ export class QuickReply {
632 const message = dom.querySelector('#qr--modal-message');626 const message = dom.querySelector('#qr--modal-message');
633 this.editorMessage = message;627 this.editorMessage = message;
634 message.value = this.message;628 message.value = this.message;
635 const updateMessageDebounced = debounce((value)=>this.updateMessage(value), 10);629 const updateMessageDebounced = debounce((value) => this.updateMessage(value), 10);
636 message.addEventListener('input', () => {630 message.addEventListener('input', () => {
637 updateMessageDebounced(message.value);631 updateMessageDebounced(message.value);
638 updateScrollDebounced();632 updateScrollDebounced();
639 }, { passive:true });633 }, { passive: true });
640 const getLineStart = ()=>{634 const getLineStart = () => {
641 const start = message.selectionStart;635 const start = message.selectionStart;
642 let lineStart;636 let lineStart;
643 if (start == 0 || message.value[start - 1] == '\n') {637 if (start == 0 || message.value[start - 1] == '\n') {
@@ -651,7 +645,7 @@ export class QuickReply {
651 }645 }
652 return lineStart;646 return lineStart;
653 };647 };
654 message.addEventListener('keydown', async(evt) => {648 message.addEventListener('keydown', async (evt) => {
655 if (this.isExecuting) return;649 if (this.isExecuting) return;
656 if (evt.key == 'Tab' && !evt.shiftKey && !evt.ctrlKey && !evt.altKey) {650 if (evt.key == 'Tab' && !evt.shiftKey && !evt.ctrlKey && !evt.altKey) {
657 // increase indent651 // increase indent
@@ -668,13 +662,13 @@ export class QuickReply {
668 document.execCommand('insertText', false, `\t${affectedLines.join('\n\t')}`);662 document.execCommand('insertText', false, `\t${affectedLines.join('\n\t')}`);
669 message.selectionStart = start + 1;663 message.selectionStart = start + 1;
670 message.selectionEnd = end + affectedLines.length;664 message.selectionEnd = end + affectedLines.length;
671 message.dispatchEvent(new Event('input', { bubbles:true }));665 message.dispatchEvent(new Event('input', { bubbles: true }));
672 } else if (!(ac.isReplaceable && ac.isActive)) {666 } else if (!(ac.isReplaceable && ac.isActive)) {
673 evt.stopImmediatePropagation();667 evt.stopImmediatePropagation();
674 evt.stopPropagation();668 evt.stopPropagation();
675 // document.execCommand is deprecated (and potentially buggy in some browsers) but the only way to retain undo-history669 // document.execCommand is deprecated (and potentially buggy in some browsers) but the only way to retain undo-history
676 document.execCommand('insertText', false, '\t');670 document.execCommand('insertText', false, '\t');
677 message.dispatchEvent(new Event('input', { bubbles:true }));671 message.dispatchEvent(new Event('input', { bubbles: true }));
678 }672 }
679 } else if (evt.key == 'Tab' && evt.shiftKey && !evt.ctrlKey && !evt.altKey) {673 } else if (evt.key == 'Tab' && evt.shiftKey && !evt.ctrlKey && !evt.altKey) {
680 // decrease indent674 // decrease indent
@@ -686,7 +680,7 @@ export class QuickReply {
686 const lineStart = getLineStart();680 const lineStart = getLineStart();
687 message.selectionStart = lineStart;681 message.selectionStart = lineStart;
688 const affectedLines = message.value.substring(lineStart, end).split('\n');682 const affectedLines = message.value.substring(lineStart, end).split('\n');
689 const newText = affectedLines.map(it=>it.replace(/^\t/, '')).join('\n');683 const newText = affectedLines.map(it => it.replace(/^\t/, '')).join('\n');
690 const delta = affectedLines.join('\n').length - newText.length;684 const delta = affectedLines.join('\n').length - newText.length;
691 // document.execCommand is deprecated (and potentially buggy in some browsers) but the only way to retain undo-history685 // document.execCommand is deprecated (and potentially buggy in some browsers) but the only way to retain undo-history
692 if (delta > 0) {686 if (delta > 0) {
@@ -697,7 +691,7 @@ export class QuickReply {
697 }691 }
698 message.selectionStart = start - (affectedLines[0].startsWith('\t') ? 1 : 0);692 message.selectionStart = start - (affectedLines[0].startsWith('\t') ? 1 : 0);
699 message.selectionEnd = end - delta;693 message.selectionEnd = end - delta;
700 message.dispatchEvent(new Event('input', { bubbles:true }));694 message.dispatchEvent(new Event('input', { bubbles: true }));
701 } else {695 } else {
702 message.selectionStart = start;696 message.selectionStart = start;
703 }697 }
@@ -714,7 +708,7 @@ export class QuickReply {
714 document.execCommand('insertText', false, `\n${indent}`);708 document.execCommand('insertText', false, `\n${indent}`);
715 message.selectionStart = start + 1 + indent.length;709 message.selectionStart = start + 1 + indent.length;
716 message.selectionEnd = message.selectionStart;710 message.selectionEnd = message.selectionStart;
717 message.dispatchEvent(new Event('input', { bubbles:true }));711 message.dispatchEvent(new Event('input', { bubbles: true }));
718 }712 }
719 } else if (evt.key == 'Enter' && evt.ctrlKey && !evt.shiftKey && !evt.altKey) {713 } else if (evt.key == 'Enter' && evt.ctrlKey && !evt.shiftKey && !evt.altKey) {
720 if (executeShortcut.checked) {714 if (executeShortcut.checked) {
@@ -751,7 +745,7 @@ export class QuickReply {
751 parser.parse(message.value, false);745 parser.parse(message.value, false);
752 const start = message.selectionStart;746 const start = message.selectionStart;
753 const end = message.selectionEnd;747 const end = message.selectionEnd;
754 const comment = parser.commandIndex.findLast(it=>it.name == '*' && (it.start <= start && it.end >= start || it.start <= end && it.end >= end));748 const comment = parser.commandIndex.findLast(it => it.name == '*' && (it.start <= start && it.end >= start || it.start <= end && it.end >= end));
755 if (comment) {749 if (comment) {
756 // uncomment750 // uncomment
757 let content = message.value.slice(comment.start + 1, comment.end - 1);751 let content = message.value.slice(comment.start + 1, comment.end - 1);
@@ -778,15 +772,15 @@ export class QuickReply {
778 message.selectionStart = start + 3;772 message.selectionStart = start + 3;
779 message.selectionEnd = end + 3;773 message.selectionEnd = end + 3;
780 }774 }
781 message.dispatchEvent(new Event('input', { bubbles:true }));775 message.dispatchEvent(new Event('input', { bubbles: true }));
782 }776 }
783 });777 });
784 const ac = await setSlashCommandAutoComplete(message, true);778 const ac = await setSlashCommandAutoComplete(message, true);
785 message.addEventListener('wheel', (evt)=>{779 message.addEventListener('wheel', (evt) => {
786 updateScrollDebounced(evt);780 updateScrollDebounced(evt);
787 });781 });
788 // @ts-ignore782 // @ts-ignore
789 message.addEventListener('scroll', (evt)=>{783 message.addEventListener('scroll', (evt) => {
790 updateScrollDebounced();784 updateScrollDebounced();
791 });785 });
792 let preBreakPointStart;786 let preBreakPointStart;
@@ -794,7 +788,7 @@ export class QuickReply {
794 /**788 /**
795 * @param {SlashCommandBreakPoint} bp789 * @param {SlashCommandBreakPoint} bp
796 */790 */
797 const removeBreakpoint = (bp)=>{791 const removeBreakpoint = (bp) => {
798 // start at -1 because "/" is not included in start-end792 // start at -1 because "/" is not included in start-end
799 let start = bp.start - 1;793 let start = bp.start - 1;
800 // step left until forward slash "/"794 // step left until forward slash "/"
@@ -812,7 +806,7 @@ export class QuickReply {
812 message.selectionEnd = end;806 message.selectionEnd = end;
813 // document.execCommand is deprecated (and potentially buggy in some browsers) but the only way to retain undo-history807 // document.execCommand is deprecated (and potentially buggy in some browsers) but the only way to retain undo-history
814 document.execCommand('insertText', false, '');808 document.execCommand('insertText', false, '');
815 message.dispatchEvent(new Event('input', { bubbles:true }));809 message.dispatchEvent(new Event('input', { bubbles: true }));
816 let postStart = preBreakPointStart;810 let postStart = preBreakPointStart;
817 let postEnd = preBreakPointEnd;811 let postEnd = preBreakPointEnd;
818 // set caret back to where it was812 // set caret back to where it was
@@ -834,12 +828,12 @@ export class QuickReply {
834 // selection end was behind breakpoint: move back by length of removed string828 // selection end was behind breakpoint: move back by length of removed string
835 postEnd = preBreakPointEnd - (end - start);829 postEnd = preBreakPointEnd - (end - start);
836 }830 }
837 return { start:postStart, end:postEnd };831 return { start: postStart, end: postEnd };
838 };832 };
839 /**833 /**
840 * @param {SlashCommandExecutor} cmd834 * @param {SlashCommandExecutor} cmd
841 */835 */
842 const addBreakpoint = (cmd)=>{836 const addBreakpoint = (cmd) => {
843 // start at -1 because "/" is not included in start-end837 // start at -1 because "/" is not included in start-end
844 let start = cmd.start - 1;838 let start = cmd.start - 1;
845 let indent = '';839 let indent = '';
@@ -860,16 +854,16 @@ export class QuickReply {
860 message.selectionEnd = start;854 message.selectionEnd = start;
861 // document.execCommand is deprecated (and potentially buggy in some browsers) but the only way to retain undo-history855 // document.execCommand is deprecated (and potentially buggy in some browsers) but the only way to retain undo-history
862 document.execCommand('insertText', false, breakpointText);856 document.execCommand('insertText', false, breakpointText);
863 message.dispatchEvent(new Event('input', { bubbles:true }));857 message.dispatchEvent(new Event('input', { bubbles: true }));
864 return breakpointText.length;858 return breakpointText.length;
865 };859 };
866 const toggleBreakpoint = ()=>{860 const toggleBreakpoint = () => {
867 const idx = message.selectionStart;861 const idx = message.selectionStart;
868 let postStart = preBreakPointStart;862 let postStart = preBreakPointStart;
869 let postEnd = preBreakPointEnd;863 let postEnd = preBreakPointEnd;
870 const parser = new SlashCommandParser();864 const parser = new SlashCommandParser();
871 parser.parse(message.value, false);865 parser.parse(message.value, false);
872 const cmdIdx = parser.commandIndex.findLastIndex(it=>it.start <= idx);866 const cmdIdx = parser.commandIndex.findLastIndex(it => it.start <= idx);
873 if (cmdIdx > -1) {867 if (cmdIdx > -1) {
874 const cmd = parser.commandIndex[cmdIdx];868 const cmd = parser.commandIndex[cmdIdx];
875 if (cmd instanceof SlashCommandBreakPoint) {869 if (cmd instanceof SlashCommandBreakPoint) {
@@ -891,12 +885,12 @@ export class QuickReply {
891 message.selectionEnd = postEnd;885 message.selectionEnd = postEnd;
892 }886 }
893 };887 };
894 message.addEventListener('pointerdown', (evt)=>{888 message.addEventListener('pointerdown', (evt) => {
895 if (!evt.ctrlKey || !evt.altKey) return;889 if (!evt.ctrlKey || !evt.altKey) return;
896 preBreakPointStart = message.selectionStart;890 preBreakPointStart = message.selectionStart;
897 preBreakPointEnd = message.selectionEnd;891 preBreakPointEnd = message.selectionEnd;
898 });892 });
899 message.addEventListener('pointerup', async(evt)=>{893 message.addEventListener('pointerup', async (evt) => {
900 if (!evt.ctrlKey || !evt.altKey || message.selectionStart != message.selectionEnd) return;894 if (!evt.ctrlKey || !evt.altKey || message.selectionStart != message.selectionEnd) return;
901 toggleBreakpoint();895 toggleBreakpoint();
902 });896 });
@@ -910,11 +904,11 @@ export class QuickReply {
910 });904 });
911 window.addEventListener('resize', resizeListener);905 window.addEventListener('resize', resizeListener);
912 updateSyntaxEnabled();906 updateSyntaxEnabled();
913 const updateSyntax = ()=>{907 const updateSyntax = () => {
914 if (messageSyntaxInner && syntax.checked) {908 if (messageSyntaxInner && syntax.checked) {
915 morphdom(909 morphdom(
916 messageSyntaxInner,910 messageSyntaxInner,
917 `<div>${hljs.highlight(`${message.value}${message.value.slice(-1) == '\n' ? ' ' : ''}`, { language:'stscript', ignoreIllegals:true })?.value}</div>`,911 `<div>${hljs.highlight(`${message.value}${message.value.slice(-1) == '\n' ? ' ' : ''}`, { language: 'stscript', ignoreIllegals: true })?.value}</div>`,
918 { childrenOnly: true },912 { childrenOnly: true },
919 );913 );
920 updateScrollDebounced();914 updateScrollDebounced();
@@ -924,7 +918,7 @@ export class QuickReply {
924 const fpsTime = 1000 / 30;918 const fpsTime = 1000 / 30;
925 let lastMessageValue = null;919 let lastMessageValue = null;
926 let wasSyntax = null;920 let wasSyntax = null;
927 const updateSyntaxLoop = ()=>{921 const updateSyntaxLoop = () => {
928 const now = Date.now();922 const now = Date.now();
929 // fps limit923 // fps limit
930 if (now - lastSyntaxUpdate < fpsTime) return requestAnimationFrame(updateSyntaxLoop);924 if (now - lastSyntaxUpdate < fpsTime) return requestAnimationFrame(updateSyntaxLoop);
@@ -945,7 +939,7 @@ export class QuickReply {
945 updateSyntax();939 updateSyntax();
946 requestAnimationFrame(updateSyntaxLoop);940 requestAnimationFrame(updateSyntaxLoop);
947 };941 };
948 requestAnimationFrame(()=>updateSyntaxLoop());942 requestAnimationFrame(() => updateSyntaxLoop());
949 message.style.setProperty('text-shadow', 'none', 'important');943 message.style.setProperty('text-shadow', 'none', 'important');
950 updateWrap();944 updateWrap();
951 updateTabSize();945 updateTabSize();
@@ -955,7 +949,7 @@ export class QuickReply {
955 const tpl = dom.querySelector('#qr--ctxItem');949 const tpl = dom.querySelector('#qr--ctxItem');
956 const linkList = dom.querySelector('#qr--ctxEditor');950 const linkList = dom.querySelector('#qr--ctxEditor');
957 const fillQrSetSelect = (/**@type {HTMLSelectElement}*/select, /**@type {QuickReplyContextLink}*/ link) => {951 const fillQrSetSelect = (/**@type {HTMLSelectElement}*/select, /**@type {QuickReplyContextLink}*/ link) => {
958 [{ name: 'Select a QR set' }, ...QuickReplySet.list.toSorted((a,b)=>a.name.toLowerCase().localeCompare(b.name.toLowerCase()))].forEach(qrs => {952 [{ name: 'Select a QR set' }, ...QuickReplySet.list.toSorted((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase()))].forEach(qrs => {
959 const opt = document.createElement('option'); {953 const opt = document.createElement('option'); {
960 opt.value = qrs.name;954 opt.value = qrs.name;
961 opt.textContent = qrs.name;955 opt.textContent = qrs.name;
@@ -1002,7 +996,7 @@ export class QuickReply {
1002 addCtxItem(link, this.contextList.length - 1);996 addCtxItem(link, this.contextList.length - 1);
1003 });997 });
1004 const onContextSort = () => {998 const onContextSort = () => {
1005 this.contextList = Array.from(linkList.querySelectorAll('.qr--ctxItem')).map((it,idx) => {999 this.contextList = Array.from(linkList.querySelectorAll('.qr--ctxItem')).map((it, idx) => {
1006 const link = this.contextList[Number(it.getAttribute('data-order'))];1000 const link = this.contextList[Number(it.getAttribute('data-order'))];
1007 it.setAttribute('data-order', String(idx));1001 it.setAttribute('data-order', String(idx));
1008 return link;1002 return link;
@@ -1019,63 +1013,63 @@ export class QuickReply {
1019 /**@type {HTMLInputElement}*/1013 /**@type {HTMLInputElement}*/
1020 const preventAutoExecute = dom.querySelector('#qr--preventAutoExecute');1014 const preventAutoExecute = dom.querySelector('#qr--preventAutoExecute');
1021 preventAutoExecute.checked = this.preventAutoExecute;1015 preventAutoExecute.checked = this.preventAutoExecute;
1022 preventAutoExecute.addEventListener('click', ()=>{1016 preventAutoExecute.addEventListener('click', () => {
1023 this.preventAutoExecute = preventAutoExecute.checked;1017 this.preventAutoExecute = preventAutoExecute.checked;
1024 this.updateContext();1018 this.updateContext();
1025 });1019 });
1026 /**@type {HTMLInputElement}*/1020 /**@type {HTMLInputElement}*/
1027 const isHidden = dom.querySelector('#qr--isHidden');1021 const isHidden = dom.querySelector('#qr--isHidden');
1028 isHidden.checked = this.isHidden;1022 isHidden.checked = this.isHidden;
1029 isHidden.addEventListener('click', ()=>{1023 isHidden.addEventListener('click', () => {
1030 this.isHidden = isHidden.checked;1024 this.isHidden = isHidden.checked;
1031 this.updateContext();1025 this.updateContext();
1032 });1026 });
1033 /**@type {HTMLInputElement}*/1027 /**@type {HTMLInputElement}*/
1034 const executeOnStartup = dom.querySelector('#qr--executeOnStartup');1028 const executeOnStartup = dom.querySelector('#qr--executeOnStartup');
1035 executeOnStartup.checked = this.executeOnStartup;1029 executeOnStartup.checked = this.executeOnStartup;
1036 executeOnStartup.addEventListener('click', ()=>{1030 executeOnStartup.addEventListener('click', () => {
1037 this.executeOnStartup = executeOnStartup.checked;1031 this.executeOnStartup = executeOnStartup.checked;
1038 this.updateContext();1032 this.updateContext();
1039 });1033 });
1040 /**@type {HTMLInputElement}*/1034 /**@type {HTMLInputElement}*/
1041 const executeOnUser = dom.querySelector('#qr--executeOnUser');1035 const executeOnUser = dom.querySelector('#qr--executeOnUser');
1042 executeOnUser.checked = this.executeOnUser;1036 executeOnUser.checked = this.executeOnUser;
1043 executeOnUser.addEventListener('click', ()=>{1037 executeOnUser.addEventListener('click', () => {
1044 this.executeOnUser = executeOnUser.checked;1038 this.executeOnUser = executeOnUser.checked;
1045 this.updateContext();1039 this.updateContext();
1046 });1040 });
1047 /**@type {HTMLInputElement}*/1041 /**@type {HTMLInputElement}*/
1048 const executeOnAi = dom.querySelector('#qr--executeOnAi');1042 const executeOnAi = dom.querySelector('#qr--executeOnAi');
1049 executeOnAi.checked = this.executeOnAi;1043 executeOnAi.checked = this.executeOnAi;
1050 executeOnAi.addEventListener('click', ()=>{1044 executeOnAi.addEventListener('click', () => {
1051 this.executeOnAi = executeOnAi.checked;1045 this.executeOnAi = executeOnAi.checked;
1052 this.updateContext();1046 this.updateContext();
1053 });1047 });
1054 /**@type {HTMLInputElement}*/1048 /**@type {HTMLInputElement}*/
1055 const executeOnChatChange = dom.querySelector('#qr--executeOnChatChange');1049 const executeOnChatChange = dom.querySelector('#qr--executeOnChatChange');
1056 executeOnChatChange.checked = this.executeOnChatChange;1050 executeOnChatChange.checked = this.executeOnChatChange;
1057 executeOnChatChange.addEventListener('click', ()=>{1051 executeOnChatChange.addEventListener('click', () => {
1058 this.executeOnChatChange = executeOnChatChange.checked;1052 this.executeOnChatChange = executeOnChatChange.checked;
1059 this.updateContext();1053 this.updateContext();
1060 });1054 });
1061 /**@type {HTMLInputElement}*/1055 /**@type {HTMLInputElement}*/
1062 const executeOnGroupMemberDraft = dom.querySelector('#qr--executeOnGroupMemberDraft');1056 const executeOnGroupMemberDraft = dom.querySelector('#qr--executeOnGroupMemberDraft');
1063 executeOnGroupMemberDraft.checked = this.executeOnGroupMemberDraft;1057 executeOnGroupMemberDraft.checked = this.executeOnGroupMemberDraft;
1064 executeOnGroupMemberDraft.addEventListener('click', ()=>{1058 executeOnGroupMemberDraft.addEventListener('click', () => {
1065 this.executeOnGroupMemberDraft = executeOnGroupMemberDraft.checked;1059 this.executeOnGroupMemberDraft = executeOnGroupMemberDraft.checked;
1066 this.updateContext();1060 this.updateContext();
1067 });1061 });
1068 /**@type {HTMLInputElement}*/1062 /**@type {HTMLInputElement}*/
1069 const executeBeforeGeneration = dom.querySelector('#qr--executeBeforeGeneration');1063 const executeBeforeGeneration = dom.querySelector('#qr--executeBeforeGeneration');
1070 executeBeforeGeneration.checked = this.executeBeforeGeneration;1064 executeBeforeGeneration.checked = this.executeBeforeGeneration;
1071 executeBeforeGeneration.addEventListener('click', ()=>{1065 executeBeforeGeneration.addEventListener('click', () => {
1072 this.executeBeforeGeneration = executeBeforeGeneration.checked;1066 this.executeBeforeGeneration = executeBeforeGeneration.checked;
1073 this.updateContext();1067 this.updateContext();
1074 });1068 });
1075 /**@type {HTMLInputElement}*/1069 /**@type {HTMLInputElement}*/
1076 const executeOnNewChat = dom.querySelector('#qr--executeOnNewChat');1070 const executeOnNewChat = dom.querySelector('#qr--executeOnNewChat');
1077 executeOnNewChat.checked = this.executeOnNewChat;1071 executeOnNewChat.checked = this.executeOnNewChat;
1078 executeOnNewChat.addEventListener('click', ()=>{1072 executeOnNewChat.addEventListener('click', () => {
1079 this.executeOnNewChat = executeOnNewChat.checked;1073 this.executeOnNewChat = executeOnNewChat.checked;
1080 this.updateContext();1074 this.updateContext();
1081 });1075 });
@@ -1102,13 +1096,13 @@ export class QuickReply {
1102 /**@type {HTMLElement}*/1096 /**@type {HTMLElement}*/
1103 const executeBtn = dom.querySelector('#qr--modal-execute');1097 const executeBtn = dom.querySelector('#qr--modal-execute');
1104 this.editorExecuteBtn = executeBtn;1098 this.editorExecuteBtn = executeBtn;
1105 executeBtn.addEventListener('click', async()=>{1099 executeBtn.addEventListener('click', async () => {
1106 await this.executeFromEditor();1100 await this.executeFromEditor();
1107 });1101 });
1108 /**@type {HTMLElement}*/1102 /**@type {HTMLElement}*/
1109 const executeBtnPause = dom.querySelector('#qr--modal-pause');1103 const executeBtnPause = dom.querySelector('#qr--modal-pause');
1110 this.editorExecuteBtnPause = executeBtnPause;1104 this.editorExecuteBtnPause = executeBtnPause;
1111 executeBtnPause.addEventListener('click', async()=>{1105 executeBtnPause.addEventListener('click', async () => {
1112 if (this.abortController) {1106 if (this.abortController) {
1113 if (this.abortController.signal.paused) {1107 if (this.abortController.signal.paused) {
1114 this.abortController.continue('Continue button clicked');1108 this.abortController.continue('Continue button clicked');
@@ -1122,7 +1116,7 @@ export class QuickReply {
1122 /**@type {HTMLElement}*/1116 /**@type {HTMLElement}*/
1123 const executeBtnStop = dom.querySelector('#qr--modal-stop');1117 const executeBtnStop = dom.querySelector('#qr--modal-stop');
1124 this.editorExecuteBtnStop = executeBtnStop;1118 this.editorExecuteBtnStop = executeBtnStop;
1125 executeBtnStop.addEventListener('click', async()=>{1119 executeBtnStop.addEventListener('click', async () => {
1126 this.abortController?.abort('Stop button clicked');1120 this.abortController?.abort('Stop button clicked');
1127 });1121 });
11281122
@@ -1131,49 +1125,49 @@ export class QuickReply {
1131 const inputMirror = dom.querySelector('#qr--modal-send_textarea');1125 const inputMirror = dom.querySelector('#qr--modal-send_textarea');
1132 // @ts-ignore1126 // @ts-ignore
1133 inputMirror.value = inputOg.value;1127 inputMirror.value = inputOg.value;
1134 const inputOgMo = new MutationObserver(muts=>{1128 const inputOgMo = new MutationObserver(muts => {
1135 if (muts.find(it=>[...it.removedNodes].includes(inputMirror) || [...it.removedNodes].find(n=>n.contains(inputMirror)))) {1129 if (muts.find(it => [...it.removedNodes].includes(inputMirror) || [...it.removedNodes].find(n => n.contains(inputMirror)))) {
1136 inputOg.removeEventListener('input', inputOgListener);1130 inputOg.removeEventListener('input', inputOgListener);
1137 }1131 }
1138 });1132 });
1139 inputOgMo.observe(document.body, { childList:true });1133 inputOgMo.observe(document.body, { childList: true });
1140 const inputOgListener = ()=>{1134 const inputOgListener = () => {
1141 // @ts-ignore1135 // @ts-ignore
1142 inputMirror.value = inputOg.value;1136 inputMirror.value = inputOg.value;
1143 };1137 };
1144 inputOg.addEventListener('input', inputOgListener);1138 inputOg.addEventListener('input', inputOgListener);
1145 inputMirror.addEventListener('input', ()=>{1139 inputMirror.addEventListener('input', () => {
1146 // @ts-ignore1140 // @ts-ignore
1147 inputOg.value = inputMirror.value;1141 inputOg.value = inputMirror.value;
1148 });1142 });
11491143
1150 /**@type {HTMLElement}*/1144 /**@type {HTMLElement}*/
1151 const resumeBtn = dom.querySelector('#qr--modal-resume');1145 const resumeBtn = dom.querySelector('#qr--modal-resume');
1152 resumeBtn.addEventListener('click', ()=>{1146 resumeBtn.addEventListener('click', () => {
1153 this.debugController?.resume();1147 this.debugController?.resume();
1154 });1148 });
1155 /**@type {HTMLElement}*/1149 /**@type {HTMLElement}*/
1156 const stepBtn = dom.querySelector('#qr--modal-step');1150 const stepBtn = dom.querySelector('#qr--modal-step');
1157 stepBtn.addEventListener('click', ()=>{1151 stepBtn.addEventListener('click', () => {
1158 this.debugController?.step();1152 this.debugController?.step();
1159 });1153 });
1160 /**@type {HTMLElement}*/1154 /**@type {HTMLElement}*/
1161 const stepIntoBtn = dom.querySelector('#qr--modal-stepInto');1155 const stepIntoBtn = dom.querySelector('#qr--modal-stepInto');
1162 stepIntoBtn.addEventListener('click', ()=>{1156 stepIntoBtn.addEventListener('click', () => {
1163 this.debugController?.stepInto();1157 this.debugController?.stepInto();
1164 });1158 });
1165 /**@type {HTMLElement}*/1159 /**@type {HTMLElement}*/
1166 const stepOutBtn = dom.querySelector('#qr--modal-stepOut');1160 const stepOutBtn = dom.querySelector('#qr--modal-stepOut');
1167 stepOutBtn.addEventListener('click', ()=>{1161 stepOutBtn.addEventListener('click', () => {
1168 this.debugController?.stepOut();1162 this.debugController?.stepOut();
1169 });1163 });
1170 /**@type {HTMLElement}*/1164 /**@type {HTMLElement}*/
1171 const minimizeBtn = dom.querySelector('#qr--modal-minimize');1165 const minimizeBtn = dom.querySelector('#qr--modal-minimize');
1172 minimizeBtn.addEventListener('click', ()=>{1166 minimizeBtn.addEventListener('click', () => {
1173 this.editorDom.classList.add('qr--minimized');1167 this.editorDom.classList.add('qr--minimized');
1174 });1168 });
1175 const maximizeBtn = dom.querySelector('#qr--modal-maximize');1169 const maximizeBtn = dom.querySelector('#qr--modal-maximize');
1176 maximizeBtn.addEventListener('click', ()=>{1170 maximizeBtn.addEventListener('click', () => {
1177 this.editorDom.classList.remove('qr--minimized');1171 this.editorDom.classList.remove('qr--minimized');
1178 });1172 });
1179 /**@type {boolean}*/1173 /**@type {boolean}*/
@@ -1182,23 +1176,23 @@ export class QuickReply {
1182 let wStart;1176 let wStart;
1183 /**@type {HTMLElement}*/1177 /**@type {HTMLElement}*/
1184 const resizeHandle = dom.querySelector('#qr--resizeHandle');1178 const resizeHandle = dom.querySelector('#qr--resizeHandle');
1185 resizeHandle.addEventListener('pointerdown', (evt)=>{1179 resizeHandle.addEventListener('pointerdown', (evt) => {
1186 if (isResizing) return;1180 if (isResizing) return;
1187 isResizing = true;1181 isResizing = true;
1188 evt.preventDefault();1182 evt.preventDefault();
1189 resizeStart = evt.x;1183 resizeStart = evt.x;
1190 // @ts-ignore1184 // @ts-ignore
1191 wStart = dom.querySelector('#qr--qrOptions').offsetWidth;1185 wStart = dom.querySelector('#qr--qrOptions').offsetWidth;
1192 const dragListener = debounce((evt)=>{1186 const dragListener = debounce((evt) => {
1193 const w = wStart + resizeStart - evt.x;1187 const w = wStart + resizeStart - evt.x;
1194 // @ts-ignore1188 // @ts-ignore
1195 dom.querySelector('#qr--qrOptions').style.setProperty('--width', `${w}px`);1189 dom.querySelector('#qr--qrOptions').style.setProperty('--width', `${w}px`);
1196 }, 5);1190 }, 5);
1197 window.addEventListener('pointerup', ()=>{1191 window.addEventListener('pointerup', () => {
1198 // @ts-ignore1192 // @ts-ignore
1199 window.removeEventListener('pointermove', dragListener);1193 window.removeEventListener('pointermove', dragListener);
1200 isResizing = false;1194 isResizing = false;
1201 }, { once:true });1195 }, { once: true });
1202 // @ts-ignore1196 // @ts-ignore
1203 window.addEventListener('pointermove', dragListener);1197 window.addEventListener('pointermove', dragListener);
1204 });1198 });
@@ -1221,13 +1215,13 @@ export class QuickReply {
1221 }1215 }
1222 this.clone.style.position = 'fixed';1216 this.clone.style.position = 'fixed';
1223 this.clone.style.visibility = 'hidden';1217 this.clone.style.visibility = 'hidden';
1224 const mo = new MutationObserver(muts=>{1218 const mo = new MutationObserver(muts => {
1225 if (muts.find(it=>[...it.removedNodes].includes(this.editorMessage) || [...it.removedNodes].find(n=>n.contains(this.editorMessage)))) {1219 if (muts.find(it => [...it.removedNodes].includes(this.editorMessage) || [...it.removedNodes].find(n => n.contains(this.editorMessage)))) {
1226 this.clone?.remove();1220 this.clone?.remove();
1227 this.clone = null;1221 this.clone = null;
1228 }1222 }
1229 });1223 });
1230 mo.observe(document.body, { childList:true });1224 mo.observe(document.body, { childList: true });
1231 }1225 }
1232 document.body.append(this.clone);1226 document.body.append(this.clone);
1233 this.clone.style.width = `${inputRect.width}px`;1227 this.clone.style.width = `${inputRect.width}px`;
@@ -1258,7 +1252,7 @@ export class QuickReply {
1258 }1252 }
1259 async executeFromEditor() {1253 async executeFromEditor() {
1260 if (this.isExecuting) return;1254 if (this.isExecuting) return;
1261 this.editorPopup.onClosing = ()=>false;1255 this.editorPopup.onClosing = () => false;
1262 const uuidCheck = /^[0-9a-z]{8}(-[0-9a-z]{4}){3}-[0-9a-z]{12}$/;1256 const uuidCheck = /^[0-9a-z]{8}(-[0-9a-z]{4}){3}-[0-9a-z]{12}$/;
1263 const oText = this.message;1257 const oText = this.message;
1264 this.isExecuting = true;1258 this.isExecuting = true;
@@ -1298,19 +1292,19 @@ export class QuickReply {
1298 });1292 });
1299 };1293 };
1300 const updateScrollDebounced = updateScroll;1294 const updateScrollDebounced = updateScroll;
1301 syntax.addEventListener('wheel', (evt)=>{1295 syntax.addEventListener('wheel', (evt) => {
1302 updateScrollDebounced(evt);1296 updateScrollDebounced(evt);
1303 });1297 });
1304 // @ts-ignore1298 // @ts-ignore
1305 syntax.addEventListener('scroll', (evt)=>{1299 syntax.addEventListener('scroll', (evt) => {
1306 updateScrollDebounced();1300 updateScrollDebounced();
1307 });1301 });
1308 try {1302 try {
1309 this.abortController = new SlashCommandAbortController();1303 this.abortController = new SlashCommandAbortController();
1310 this.debugController = new SlashCommandDebugController();1304 this.debugController = new SlashCommandDebugController();
1311 this.debugController.onBreakPoint = async(closure, executor)=>{1305 this.debugController.onBreakPoint = async (closure, executor) => {
1312 this.editorDom.classList.add('qr--isPaused');1306 this.editorDom.classList.add('qr--isPaused');
1313 syntax.innerHTML = hljs.highlight(`${closure.fullText}${closure.fullText.slice(-1) == '\n' ? ' ' : ''}`, { language:'stscript', ignoreIllegals:true })?.value;1307 syntax.innerHTML = hljs.highlight(`${closure.fullText}${closure.fullText.slice(-1) == '\n' ? ' ' : ''}`, { language: 'stscript', ignoreIllegals: true })?.value;
1314 this.editorMessageLabel.innerHTML = '';1308 this.editorMessageLabel.innerHTML = '';
1315 if (uuidCheck.test(closure.source)) {1309 if (uuidCheck.test(closure.source)) {
1316 const p0 = document.createElement('span'); {1310 const p0 = document.createElement('span'); {
@@ -1318,7 +1312,7 @@ export class QuickReply {
1318 this.editorMessageLabel.append(p0);1312 this.editorMessageLabel.append(p0);
1319 }1313 }
1320 const p1 = document.createElement('strong'); {1314 const p1 = document.createElement('strong'); {
1321 p1.textContent = executor.source.slice(0,5);1315 p1.textContent = executor.source.slice(0, 5);
1322 this.editorMessageLabel.append(p1);1316 this.editorMessageLabel.append(p1);
1323 }1317 }
1324 const p2 = document.createElement('span'); {1318 const p2 = document.createElement('span'); {
@@ -1340,7 +1334,7 @@ export class QuickReply {
1340 /**1334 /**
1341 * @param {SlashCommandScope} scope1335 * @param {SlashCommandScope} scope
1342 */1336 */
1343 const buildVars = (scope, isCurrent = false)=>{1337 const buildVars = (scope, isCurrent = false) => {
1344 if (!isCurrent) {1338 if (!isCurrent) {
1345 ci--;1339 ci--;
1346 }1340 }
@@ -1358,7 +1352,7 @@ export class QuickReply {
1358 }1352 }
1359 wrap.append(namedTitle);1353 wrap.append(namedTitle);
1360 }1354 }
1361 const keys = new Set([...Object.keys(this.debugController.namedArguments ?? {}), ...(executor.namedArgumentList ?? []).map(it=>it.name)]);1355 const keys = new Set([...Object.keys(this.debugController.namedArguments ?? {}), ...(executor.namedArgumentList ?? []).map(it => it.name)]);
1362 for (const key of keys) {1356 for (const key of keys) {
1363 if (key[0] == '_') continue;1357 if (key[0] == '_') continue;
1364 const item = document.createElement('div'); {1358 const item = document.createElement('div'); {
@@ -1371,7 +1365,7 @@ export class QuickReply {
1371 const vUnresolved = document.createElement('div'); {1365 const vUnresolved = document.createElement('div'); {
1372 vUnresolved.classList.add('qr--val');1366 vUnresolved.classList.add('qr--val');
1373 vUnresolved.classList.add('qr--singleCol');1367 vUnresolved.classList.add('qr--singleCol');
1374 const val = executor.namedArgumentList.find(it=>it.name == key)?.value;1368 const val = executor.namedArgumentList.find(it => it.name == key)?.value;
1375 if (val instanceof SlashCommandClosure) {1369 if (val instanceof SlashCommandClosure) {
1376 vUnresolved.classList.add('qr--closure');1370 vUnresolved.classList.add('qr--closure');
1377 vUnresolved.title = val.rawText;1371 vUnresolved.title = val.rawText;
@@ -1437,7 +1431,7 @@ export class QuickReply {
1437 // @ts-ignore1431 // @ts-ignore
1438 while (unnamed.length < executor.unnamedArgumentList?.length ?? 0) unnamed.push(undefined);1432 while (unnamed.length < executor.unnamedArgumentList?.length ?? 0) unnamed.push(undefined);
1439 // @ts-ignore1433 // @ts-ignore
1440 unnamed = unnamed.map((it,idx)=>[executor.unnamedArgumentList?.[idx], it]);1434 unnamed = unnamed.map((it, idx) => [executor.unnamedArgumentList?.[idx], it]);
1441 // @ts-ignore1435 // @ts-ignore
1442 for (const arg of unnamed) {1436 for (const arg of unnamed) {
1443 i++;1437 i++;
@@ -1511,7 +1505,7 @@ export class QuickReply {
1511 title.textContent = isCurrent ? 'Current Scope' : 'Parent Scope';1505 title.textContent = isCurrent ? 'Current Scope' : 'Parent Scope';
1512 if (c.source == source) {1506 if (c.source == source) {
1513 let hi;1507 let hi;
1514 title.addEventListener('pointerenter', ()=>{1508 title.addEventListener('pointerenter', () => {
1515 const loc = this.getEditorPosition(Math.max(0, c.executorList[0].start - 1), c.executorList.slice(-1)[0].end, c.fullText);1509 const loc = this.getEditorPosition(Math.max(0, c.executorList[0].start - 1), c.executorList.slice(-1)[0].end, c.fullText);
1516 const layer = syntax.getBoundingClientRect();1510 const layer = syntax.getBoundingClientRect();
1517 hi = document.createElement('div');1511 hi = document.createElement('div');
@@ -1522,7 +1516,7 @@ export class QuickReply {
1522 hi.style.height = `${loc.bottom - loc.top}px`;1516 hi.style.height = `${loc.bottom - loc.top}px`;
1523 syntax.append(hi);1517 syntax.append(hi);
1524 });1518 });
1525 title.addEventListener('pointerleave', ()=>hi?.remove());1519 title.addEventListener('pointerleave', () => hi?.remove());
1526 }1520 }
1527 wrap.append(title);1521 wrap.append(title);
1528 }1522 }
@@ -1635,7 +1629,7 @@ export class QuickReply {
1635 }1629 }
1636 return wrap;1630 return wrap;
1637 };1631 };
1638 const buildStack = ()=>{1632 const buildStack = () => {
1639 const wrap = document.createElement('div'); {1633 const wrap = document.createElement('div'); {
1640 wrap.classList.add('qr--stack');1634 wrap.classList.add('qr--stack');
1641 const title = document.createElement('div'); {1635 const title = document.createElement('div'); {
@@ -1651,7 +1645,7 @@ export class QuickReply {
1651 item.classList.add('qr--item');1645 item.classList.add('qr--item');
1652 if (executor.source == source) {1646 if (executor.source == source) {
1653 let hi;1647 let hi;
1654 item.addEventListener('pointerenter', ()=>{1648 item.addEventListener('pointerenter', () => {
1655 const loc = this.getEditorPosition(Math.max(0, executor.start - 1), executor.end, c.fullText);1649 const loc = this.getEditorPosition(Math.max(0, executor.start - 1), executor.end, c.fullText);
1656 const layer = syntax.getBoundingClientRect();1650 const layer = syntax.getBoundingClientRect();
1657 hi = document.createElement('div');1651 hi = document.createElement('div');
@@ -1662,7 +1656,7 @@ export class QuickReply {
1662 hi.style.height = `${loc.bottom - loc.top}px`;1656 hi.style.height = `${loc.bottom - loc.top}px`;
1663 syntax.append(hi);1657 syntax.append(hi);
1664 });1658 });
1665 item.addEventListener('pointerleave', ()=>hi?.remove());1659 item.addEventListener('pointerleave', () => hi?.remove());
1666 }1660 }
1667 const cmd = document.createElement('div'); {1661 const cmd = document.createElement('div'); {
1668 cmd.classList.add('qr--cmd');1662 cmd.classList.add('qr--cmd');
@@ -1678,7 +1672,7 @@ export class QuickReply {
1678 if (uuidCheck.test(executor.source)) {1672 if (uuidCheck.test(executor.source)) {
1679 const p1 = document.createElement('span'); {1673 const p1 = document.createElement('span'); {
1680 p1.classList.add('qr--fixed');1674 p1.classList.add('qr--fixed');
1681 p1.textContent = executor.source.slice(0,5);1675 p1.textContent = executor.source.slice(0, 5);
1682 src.append(p1);1676 src.append(p1);
1683 }1677 }
1684 const p2 = document.createElement('span'); {1678 const p2 = document.createElement('span'); {
@@ -1756,7 +1750,7 @@ export class QuickReply {
1756 this.editorMessageLabel.innerHTML = '';1750 this.editorMessageLabel.innerHTML = '';
1757 this.editorMessageLabel.textContent = 'Message / Command: ';1751 this.editorMessageLabel.textContent = 'Message / Command: ';
1758 this.editorMessage.value = oText;1752 this.editorMessage.value = oText;
1759 this.editorMessage.dispatchEvent(new Event('input', { bubbles:true }));1753 this.editorMessage.dispatchEvent(new Event('input', { bubbles: true }));
1760 this.editorExecutePromise = null;1754 this.editorExecutePromise = null;
1761 this.editorExecuteBtn.classList.remove('qr--busy');1755 this.editorExecuteBtn.classList.remove('qr--busy');
1762 this.editorDom.classList.remove('qr--isExecuting');1756 this.editorDom.classList.remove('qr--isExecuting');
@@ -1769,8 +1763,6 @@ export class QuickReply {
1769 }1763 }
17701764
17711765
1772
1773
1774 delete() {1766 delete() {
1775 if (this.onDelete) {1767 if (this.onDelete) {
1776 this.unrender();1768 this.unrender();
@@ -1870,7 +1862,7 @@ export class QuickReply {
1870 this.updateContext();1862 this.updateContext();
1871 }1863 }
1872 removeContextLink(setName) {1864 removeContextLink(setName) {
1873 const idx = this.contextList.findIndex(it=>it.set.name == setName);1865 const idx = this.contextList.findIndex(it => it.set.name == setName);
1874 if (idx > -1) {1866 if (idx > -1) {
1875 this.contextList.splice(idx, 1);1867 this.contextList.splice(idx, 1);
1876 this.updateContext();1868 this.updateContext();
@@ -1908,8 +1900,6 @@ export class QuickReply {
1908 }1900 }
19091901
19101902
1911
1912
1913 toJSON() {1903 toJSON() {
1914 return {1904 return {
1915 id: this.id,1905 id: this.id,
public/scripts/extensions/quick-reply/src/QuickReplyConfig.js+12 -20
@@ -13,25 +13,21 @@ export class QuickReplyConfig {
13 /**@type {HTMLElement}*/ setListDom;13 /**@type {HTMLElement}*/ setListDom;
1414
1515
16
17
18 static from(props) {16 static from(props) {
19 props.setList = props.setList?.map(it=>QuickReplySetLink.from(it))?.filter(it=>it.set) ?? [];17 props.setList = props.setList?.map(it => QuickReplySetLink.from(it))?.filter(it => it.set) ?? [];
20 const instance = Object.assign(new this(), props);18 const instance = Object.assign(new this(), props);
21 instance.init();19 instance.init();
22 return instance;20 return instance;
23 }21 }
2422
2523
26
27
28 init() {24 init() {
29 this.setList.forEach(it=>this.hookQuickReplyLink(it));25 this.setList.forEach(it => this.hookQuickReplyLink(it));
30 }26 }
3127
3228
33 hasSet(qrs) {29 hasSet(qrs) {
34 return this.setList.find(it=>it.set == qrs) != null;30 return this.setList.find(it => it.set == qrs) != null;
35 }31 }
36 addSet(qrs, isVisible = true) {32 addSet(qrs, isVisible = true) {
37 if (!this.hasSet(qrs)) {33 if (!this.hasSet(qrs)) {
@@ -45,7 +41,7 @@ export class QuickReplyConfig {
45 }41 }
46 }42 }
47 removeSet(qrs) {43 removeSet(qrs) {
48 const idx = this.setList.findIndex(it=>it.set == qrs);44 const idx = this.setList.findIndex(it => it.set == qrs);
49 if (idx > -1) {45 if (idx > -1) {
50 this.setList.splice(idx, 1);46 this.setList.splice(idx, 1);
51 this.update();47 this.update();
@@ -54,13 +50,11 @@ export class QuickReplyConfig {
54 }50 }
5551
5652
57
58
59 renderSettingsInto(/**@type {HTMLElement}*/root) {53 renderSettingsInto(/**@type {HTMLElement}*/root) {
60 /**@type {HTMLElement}*/54 /**@type {HTMLElement}*/
61 this.setListDom = root.querySelector('.qr--setList');55 this.setListDom = root.querySelector('.qr--setList');
62 root.querySelector('.qr--setListAdd').addEventListener('click', ()=>{56 root.querySelector('.qr--setListAdd').addEventListener('click', () => {
63 const newSet = QuickReplySet.list.find(qr=>!this.setList.find(qrl=>qrl.set == qr));57 const newSet = QuickReplySet.list.find(qr => !this.setList.find(qrl => qrl.set == qr));
64 if (newSet) {58 if (newSet) {
65 this.addSet(newSet);59 this.addSet(newSet);
66 } else {60 } else {
@@ -74,14 +68,14 @@ export class QuickReplyConfig {
74 // @ts-ignore68 // @ts-ignore
75 $(this.setListDom).sortable({69 $(this.setListDom).sortable({
76 delay: getSortableDelay(),70 delay: getSortableDelay(),
77 stop: ()=>this.onSetListSort(),71 stop: () => this.onSetListSort(),
78 });72 });
79 this.setList.filter(it=>!it.set.isDeleted).forEach((qrl,idx)=>this.setListDom.append(qrl.renderSettings(idx)));73 this.setList.filter(it => !it.set.isDeleted).forEach((qrl, idx) => this.setListDom.append(qrl.renderSettings(idx)));
80 }74 }
8175
8276
83 onSetListSort() {77 onSetListSort() {
84 this.setList = Array.from(this.setListDom.children).map((it,idx)=>{78 this.setList = Array.from(this.setListDom.children).map((it, idx) => {
85 const qrl = this.setList[Number(it.getAttribute('data-order'))];79 const qrl = this.setList[Number(it.getAttribute('data-order'))];
86 qrl.index = idx;80 qrl.index = idx;
87 it.setAttribute('data-order', String(idx));81 it.setAttribute('data-order', String(idx));
@@ -91,15 +85,13 @@ export class QuickReplyConfig {
91 }85 }
9286
9387
94
95
96 /**88 /**
97 * @param {QuickReplySetLink} qrl89 * @param {QuickReplySetLink} qrl
98 */90 */
99 hookQuickReplyLink(qrl) {91 hookQuickReplyLink(qrl) {
100 qrl.onDelete = ()=>this.deleteQuickReplyLink(qrl);92 qrl.onDelete = () => this.deleteQuickReplyLink(qrl);
101 qrl.onUpdate = ()=>this.update();93 qrl.onUpdate = () => this.update();
102 qrl.onRequestEditSet = ()=>this.requestEditSet(qrl.set);94 qrl.onRequestEditSet = () => this.requestEditSet(qrl.set);
103 }95 }
10496
105 deleteQuickReplyLink(qrl) {97 deleteQuickReplyLink(qrl) {
public/scripts/extensions/quick-reply/src/QuickReplyContextLink.js+0 -2
@@ -8,8 +8,6 @@ export class QuickReplyContextLink {
8 }8 }
99
1010
11
12
13 /**@type {QuickReplySet}*/ set;11 /**@type {QuickReplySet}*/ set;
14 /**@type {Boolean}*/ isChained = false;12 /**@type {Boolean}*/ isChained = false;
1513
public/scripts/extensions/quick-reply/src/QuickReplySet.js+25 -26
@@ -24,7 +24,7 @@ export class QuickReplySet {
24 * @param {string} name - name of the QuickReplySet24 * @param {string} name - name of the QuickReplySet
25 */25 */
26 static get(name) {26 static get(name) {
27 return this.list.find(it=>it.name == name);27 return this.list.find(it => it.name == name);
28 }28 }
2929
30 /**@type {string}*/ name;30 /**@type {string}*/ name;
@@ -42,11 +42,11 @@ export class QuickReplySet {
42 /**@type {HTMLElement}*/ settingsDom;42 /**@type {HTMLElement}*/ settingsDom;
4343
44 constructor() {44 constructor() {
45 this.save = debounceAsync(()=>this.performSave(), 200);45 this.save = debounceAsync(() => this.performSave(), 200);
46 }46 }
4747
48 init() {48 init() {
49 this.qrList.forEach(qr=>this.hookQuickReply(qr));49 this.qrList.forEach(qr => this.hookQuickReply(qr));
50 }50 }
5151
52 unrender() {52 unrender() {
@@ -60,7 +60,7 @@ export class QuickReplySet {
60 this.dom = root;60 this.dom = root;
61 root.classList.add('qr--buttons');61 root.classList.add('qr--buttons');
62 this.updateColor();62 this.updateColor();
63 this.qrList.filter(qr=>!qr.isHidden).forEach(qr=>{63 this.qrList.filter(qr => !qr.isHidden).forEach(qr => {
64 root.append(qr.render());64 root.append(qr.render());
65 });65 });
66 }66 }
@@ -70,7 +70,7 @@ export class QuickReplySet {
70 rerender() {70 rerender() {
71 if (!this.dom) return;71 if (!this.dom) return;
72 this.dom.innerHTML = '';72 this.dom.innerHTML = '';
73 this.qrList.filter(qr=>!qr.isHidden).forEach(qr=>{73 this.qrList.filter(qr => !qr.isHidden).forEach(qr => {
74 this.dom.append(qr.render());74 this.dom.append(qr.render());
75 });75 });
76 }76 }
@@ -95,7 +95,7 @@ export class QuickReplySet {
95 if (!this.settingsDom) {95 if (!this.settingsDom) {
96 this.settingsDom = document.createElement('div'); {96 this.settingsDom = document.createElement('div'); {
97 this.settingsDom.classList.add('qr--set-qrListContents');97 this.settingsDom.classList.add('qr--set-qrListContents');
98 this.qrList.forEach((qr,idx)=>{98 this.qrList.forEach((qr, idx) => {
99 this.renderSettingsItem(qr, idx);99 this.renderSettingsItem(qr, idx);
100 });100 });
101 }101 }
@@ -138,12 +138,12 @@ export class QuickReplySet {
138 */138 */
139 async executeWithOptions(qr, options = {}) {139 async executeWithOptions(qr, options = {}) {
140 options = Object.assign({140 options = Object.assign({
141 message:null,141 message: null,
142 isAutoExecute:false,142 isAutoExecute: false,
143 isEditor:false,143 isEditor: false,
144 isRun:false,144 isRun: false,
145 scope:null,145 scope: null,
146 executionOptions:{},146 executionOptions: {},
147 }, options);147 }, options);
148 const execOptions = options.executionOptions;148 const execOptions = options.executionOptions;
149 /**@type {HTMLTextAreaElement}*/149 /**@type {HTMLTextAreaElement}*/
@@ -208,9 +208,8 @@ export class QuickReplySet {
208 }208 }
209209
210 addQuickReply(data = {}) {210 addQuickReply(data = {}) {
211 const id = Math.max(this.idIndex, this.qrList.reduce((max,qr)=>Math.max(max,qr.id),0)) + 1;211 const id = Math.max(this.idIndex, this.qrList.reduce((max, qr) => Math.max(max, qr.id), 0)) + 1;
212 data.id =212 data.id = this.idIndex = id + 1;
213 this.idIndex = id + 1;
214 const qr = QuickReply.from(data);213 const qr = QuickReply.from(data);
215 this.qrList.push(qr);214 this.qrList.push(qr);
216 this.hookQuickReply(qr);215 this.hookQuickReply(qr);
@@ -257,11 +256,11 @@ export class QuickReplySet {
257 */256 */
258 hookQuickReply(qr) {257 hookQuickReply(qr) {
259 // @ts-ignore258 // @ts-ignore
260 qr.onDebug = ()=>this.debug(qr);259 qr.onDebug = () => this.debug(qr);
261 qr.onExecute = (_, options)=>this.executeWithOptions(qr, options);260 qr.onExecute = (_, options) => this.executeWithOptions(qr, options);
262 qr.onDelete = ()=>this.removeQuickReply(qr);261 qr.onDelete = () => this.removeQuickReply(qr);
263 qr.onUpdate = ()=>this.save();262 qr.onUpdate = () => this.save();
264 qr.onInsertBefore = (qrJson)=>{263 qr.onInsertBefore = (qrJson) => {
265 this.addQuickReplyFromText(qrJson);264 this.addQuickReplyFromText(qrJson);
266 const newQr = this.qrList.pop();265 const newQr = this.qrList.pop();
267 this.qrList.splice(this.qrList.indexOf(qr), 0, newQr);266 this.qrList.splice(this.qrList.indexOf(qr), 0, newQr);
@@ -270,7 +269,7 @@ export class QuickReplySet {
270 }269 }
271 this.save();270 this.save();
272 };271 };
273 qr.onTransfer = async()=>{272 qr.onTransfer = async () => {
274 /**@type {HTMLSelectElement} */273 /**@type {HTMLSelectElement} */
275 let sel;274 let sel;
276 let isCopy = false;275 let isCopy = false;
@@ -301,14 +300,14 @@ export class QuickReplySet {
301 sel.append(opt);300 sel.append(opt);
302 }301 }
303 }302 }
304 sel.addEventListener('keyup', (evt)=>{303 sel.addEventListener('keyup', (evt) => {
305 if (evt.key == 'Shift') {304 if (evt.key == 'Shift') {
306 // @ts-ignore305 // @ts-ignore
307 (dlg.dom ?? dlg.dlg).classList.remove('qr--isCopy');306 (dlg.dom ?? dlg.dlg).classList.remove('qr--isCopy');
308 return;307 return;
309 }308 }
310 });309 });
311 sel.addEventListener('keydown', (evt)=>{310 sel.addEventListener('keydown', (evt) => {
312 if (evt.key == 'Shift') {311 if (evt.key == 'Shift') {
313 // @ts-ignore312 // @ts-ignore
314 (dlg.dom ?? dlg.dlg).classList.add('qr--isCopy');313 (dlg.dom ?? dlg.dlg).classList.add('qr--isCopy');
@@ -330,12 +329,12 @@ export class QuickReplySet {
330 dom.append(hintP);329 dom.append(hintP);
331 }330 }
332 }331 }
333 const dlg = new Popup(dom, POPUP_TYPE.CONFIRM, null, { okButton:'Transfer', cancelButton:'Cancel' });332 const dlg = new Popup(dom, POPUP_TYPE.CONFIRM, null, { okButton: 'Transfer', cancelButton: 'Cancel' });
334 const copyBtn = document.createElement('div'); {333 const copyBtn = document.createElement('div'); {
335 copyBtn.classList.add('qr--copy');334 copyBtn.classList.add('qr--copy');
336 copyBtn.classList.add('menu_button');335 copyBtn.classList.add('menu_button');
337 copyBtn.textContent = 'Copy';336 copyBtn.textContent = 'Copy';
338 copyBtn.addEventListener('click', ()=>{337 copyBtn.addEventListener('click', () => {
339 isCopy = true;338 isCopy = true;
340 dlg.completeAffirmative();339 dlg.completeAffirmative();
341 });340 });
@@ -346,7 +345,7 @@ export class QuickReplySet {
346 sel.focus();345 sel.focus();
347 await prom;346 await prom;
348 if (dlg.result == POPUP_RESULT.AFFIRMATIVE) {347 if (dlg.result == POPUP_RESULT.AFFIRMATIVE) {
349 const qrs = QuickReplySet.list.find(it=>it.name == sel.value);348 const qrs = QuickReplySet.list.find(it => it.name == sel.value);
350 qrs.addQuickReply(qr.toJSON());349 qrs.addQuickReply(qr.toJSON());
351 if (!isCopy) {350 if (!isCopy) {
352 qr.delete();351 qr.delete();
public/scripts/extensions/quick-reply/src/QuickReplySetLink.js+6 -14
@@ -9,8 +9,6 @@ export class QuickReplySetLink {
9 }9 }
1010
1111
12
13
14 /**@type {QuickReplySet}*/ set;12 /**@type {QuickReplySet}*/ set;
15 /**@type {Boolean}*/ isVisible = true;13 /**@type {Boolean}*/ isVisible = true;
1614
@@ -23,8 +21,6 @@ export class QuickReplySetLink {
23 /**@type {HTMLElement}*/ settingsDom;21 /**@type {HTMLElement}*/ settingsDom;
2422
2523
26
27
28 renderSettings(idx) {24 renderSettings(idx) {
29 this.index = idx;25 this.index = idx;
30 const item = document.createElement('div'); {26 const item = document.createElement('div'); {
@@ -40,12 +36,12 @@ export class QuickReplySetLink {
40 const set = document.createElement('select'); {36 const set = document.createElement('select'); {
41 set.classList.add('qr--set');37 set.classList.add('qr--set');
42 // fix for jQuery sortable breaking childrens' touch events38 // fix for jQuery sortable breaking childrens' touch events
43 set.addEventListener('touchstart', (evt)=>evt.stopPropagation());39 set.addEventListener('touchstart', (evt) => evt.stopPropagation());
44 set.addEventListener('change', ()=>{40 set.addEventListener('change', () => {
45 this.set = QuickReplySet.get(set.value);41 this.set = QuickReplySet.get(set.value);
46 this.update();42 this.update();
47 });43 });
48 QuickReplySet.list.toSorted((a,b)=>a.name.toLowerCase().localeCompare(b.name.toLowerCase())).forEach(qrs=>{44 QuickReplySet.list.toSorted((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase())).forEach(qrs => {
49 const opt = document.createElement('option'); {45 const opt = document.createElement('option'); {
50 opt.value = qrs.name;46 opt.value = qrs.name;
51 opt.textContent = qrs.name;47 opt.textContent = qrs.name;
@@ -61,7 +57,7 @@ export class QuickReplySetLink {
61 const cb = document.createElement('input'); {57 const cb = document.createElement('input'); {
62 cb.type = 'checkbox';58 cb.type = 'checkbox';
63 cb.checked = this.isVisible;59 cb.checked = this.isVisible;
64 cb.addEventListener('click', ()=>{60 cb.addEventListener('click', () => {
65 this.isVisible = cb.checked;61 this.isVisible = cb.checked;
66 this.update();62 this.update();
67 });63 });
@@ -76,7 +72,7 @@ export class QuickReplySetLink {
76 edit.classList.add('fa-solid');72 edit.classList.add('fa-solid');
77 edit.classList.add('fa-pencil');73 edit.classList.add('fa-pencil');
78 edit.title = 'Edit quick reply set';74 edit.title = 'Edit quick reply set';
79 edit.addEventListener('click', ()=>this.requestEditSet());75 edit.addEventListener('click', () => this.requestEditSet());
80 item.append(edit);76 item.append(edit);
81 }77 }
82 const del = document.createElement('div'); {78 const del = document.createElement('div'); {
@@ -86,7 +82,7 @@ export class QuickReplySetLink {
86 del.classList.add('fa-solid');82 del.classList.add('fa-solid');
87 del.classList.add('fa-trash-can');83 del.classList.add('fa-trash-can');
88 del.title = 'Remove quick reply set';84 del.title = 'Remove quick reply set';
89 del.addEventListener('click', ()=>this.delete());85 del.addEventListener('click', () => this.delete());
90 item.append(del);86 item.append(del);
91 }87 }
92 }88 }
@@ -98,8 +94,6 @@ export class QuickReplySetLink {
98 }94 }
9995
10096
101
102
103 update() {97 update() {
104 if (this.onUpdate) {98 if (this.onUpdate) {
105 this.onUpdate(this);99 this.onUpdate(this);
@@ -118,8 +112,6 @@ export class QuickReplySetLink {
118 }112 }
119113
120114
121
122
123 toJSON() {115 toJSON() {
124 return {116 return {
125 set: this.set.name,117 set: this.set.name,
public/scripts/extensions/quick-reply/src/QuickReplySettings.js+2 -8
@@ -15,8 +15,6 @@ export class QuickReplySettings {
15 }15 }
1616
1717
18
19
20 /**@type {Boolean}*/ isEnabled = false;18 /**@type {Boolean}*/ isEnabled = false;
21 /**@type {Boolean}*/ isCombined = false;19 /**@type {Boolean}*/ isCombined = false;
22 /**@type {Boolean}*/ isPopout = false;20 /**@type {Boolean}*/ isPopout = false;
@@ -50,8 +48,6 @@ export class QuickReplySettings {
50 /**@type {Function}*/ onRequestEditSet;48 /**@type {Function}*/ onRequestEditSet;
5149
5250
53
54
55 init() {51 init() {
56 this.hookConfig(this.config);52 this.hookConfig(this.config);
57 this.hookConfig(this.chatConfig);53 this.hookConfig(this.chatConfig);
@@ -60,8 +56,8 @@ export class QuickReplySettings {
6056
61 hookConfig(config) {57 hookConfig(config) {
62 if (config) {58 if (config) {
63 config.onUpdate = ()=>this.save();59 config.onUpdate = () => this.save();
64 config.onRequestEditSet = (qrs)=>this.requestEditSet(qrs);60 config.onRequestEditSet = (qrs) => this.requestEditSet(qrs);
65 }61 }
66 }62 }
67 unhookConfig(config) {63 unhookConfig(config) {
@@ -72,8 +68,6 @@ export class QuickReplySettings {
72 }68 }
7369
7470
75
76
77 save() {71 save() {
78 extension_settings.quickReplyV2 = this.toJSON();72 extension_settings.quickReplyV2 = this.toJSON();
79 saveSettingsDebounced();73 saveSettingsDebounced();
public/scripts/extensions/quick-reply/src/SlashCommandHandler.js+13 -21
@@ -16,15 +16,11 @@ export class SlashCommandHandler {
16 /** @type {QuickReplyApi} */ api;16 /** @type {QuickReplyApi} */ api;
1717
1818
19
20
21 constructor(/** @type {QuickReplyApi} */api) {19 constructor(/** @type {QuickReplyApi} */api) {
22 this.api = api;20 this.api = api;
23 }21 }
2422
2523
26
27
28 init() {24 init() {
29 function getExecutionIcons(/** @type {QuickReply} */ qr) {25 function getExecutionIcons(/** @type {QuickReply} */ qr) {
30 let icons = '';26 let icons = '';
@@ -56,7 +52,7 @@ export class SlashCommandHandler {
56 qrIds: (executor) => QuickReplySet.get(String(executor.namedArgumentList.find(x => x.name == 'set')?.value))?.qrList.map(qr => {52 qrIds: (executor) => QuickReplySet.get(String(executor.namedArgumentList.find(x => x.name == 'set')?.value))?.qrList.map(qr => {
57 const icons = getExecutionIcons(qr);53 const icons = getExecutionIcons(qr);
58 const message = `${qr.automationId ? `[${qr.automationId}]` : ''}${icons ? `[auto: ${icons}]` : ''} ${qr.title || qr.message}`.trim();54 const message = `${qr.automationId ? `[${qr.automationId}]` : ''}${icons ? `[auto: ${icons}]` : ''} ${qr.title || qr.message}`.trim();
59 return new SlashCommandEnumValue(qr.label, message, enumTypes.enum, enumIcons.qr, null, ()=>qr.id.toString(), true);55 return new SlashCommandEnumValue(qr.label, message, enumTypes.enum, enumIcons.qr, null, () => qr.id.toString(), true);
60 }) ?? [],56 }) ?? [],
6157
62 /** All QRs as a set.name string, to be able to execute, for example via the /run command */58 /** 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 {
352 return '';348 return '';
353 },349 },
354 returns: 'updated quick reply',350 returns: 'updated quick reply',
355 namedArgumentList: [...qrUpdateArgs, ...qrArgs.map(it=>{351 namedArgumentList: [...qrUpdateArgs, ...qrArgs.map(it => {
356 if (it.name == 'label') {352 if (it.name == 'label') {
357 const clone = SlashCommandNamedArgument.fromProps(it);353 const clone = SlashCommandNamedArgument.fromProps(it);
358 clone.isRequired = false;354 clone.isRequired = false;
@@ -691,16 +687,16 @@ export class SlashCommandHandler {
691 if (!args.from) throw new Error('/import requires from= to be set.');687 if (!args.from) throw new Error('/import requires from= to be set.');
692 if (!value) throw new Error('/import requires the unnamed argument to be set.');688 if (!value) throw new Error('/import requires the unnamed argument to be set.');
693 let qr = [...this.api.listGlobalSets(), ...this.api.listChatSets()]689 let qr = [...this.api.listGlobalSets(), ...this.api.listChatSets()]
694 .map(it=>this.api.getSetByName(it)?.qrList ?? [])690 .map(it => this.api.getSetByName(it)?.qrList ?? [])
695 .flat()691 .flat()
696 .find(it=>it.label == args.from)692 .find(it => it.label == args.from)
697 ;693 ;
698 if (!qr) {694 if (!qr) {
699 let [setName, ...qrNameParts] = args.from.split('.');695 let [setName, ...qrNameParts] = args.from.split('.');
700 let qrName = qrNameParts.join('.');696 let qrName = qrNameParts.join('.');
701 let qrs = QuickReplySet.get(setName);697 let qrs = QuickReplySet.get(setName);
702 if (qrs) {698 if (qrs) {
703 qr = qrs.qrList.find(it=>it.label == qrName);699 qr = qrs.qrList.find(it => it.label == qrName);
704 }700 }
705 }701 }
706 if (qr) {702 if (qr) {
@@ -709,23 +705,23 @@ export class SlashCommandHandler {
709 if (args._debugController) {705 if (args._debugController) {
710 closure.source = args.from;706 closure.source = args.from;
711 }707 }
712 const testCandidates = (executor)=>{708 const testCandidates = (executor) => {
713 return (709 return (
714 executor.namedArgumentList.find(arg=>arg.name == 'key')710 executor.namedArgumentList.find(arg => arg.name == 'key')
715 && executor.unnamedArgumentList.length > 0711 && executor.unnamedArgumentList.length > 0
716 && executor.unnamedArgumentList[0].value instanceof SlashCommandClosure712 && executor.unnamedArgumentList[0].value instanceof SlashCommandClosure
717 ) || (713 ) || (
718 !executor.namedArgumentList.find(arg=>arg.name == 'key')714 !executor.namedArgumentList.find(arg => arg.name == 'key')
719 && executor.unnamedArgumentList.length > 1715 && executor.unnamedArgumentList.length > 1
720 && executor.unnamedArgumentList[1].value instanceof SlashCommandClosure716 && executor.unnamedArgumentList[1].value instanceof SlashCommandClosure
721 );717 );
722 };718 };
723 const candidates = closure.executorList719 const candidates = closure.executorList
724 .filter(executor=>['let', 'var'].includes(executor.command.name))720 .filter(executor => ['let', 'var'].includes(executor.command.name))
725 .filter(testCandidates)721 .filter(testCandidates)
726 .map(executor=>({722 .map(executor => ({
727 key: executor.namedArgumentList.find(arg=>arg.name == 'key')?.value ?? executor.unnamedArgumentList[0].value,723 key: executor.namedArgumentList.find(arg => arg.name == 'key')?.value ?? executor.unnamedArgumentList[0].value,
728 value: executor.unnamedArgumentList[executor.namedArgumentList.find(arg=>arg.name == 'key') ? 0 : 1].value,724 value: executor.unnamedArgumentList[executor.namedArgumentList.find(arg => arg.name == 'key') ? 0 : 1].value,
729 }))725 }))
730 ;726 ;
731 for (let i = 0; i < value.length; i++) {727 for (let i = 0; i < value.length; i++) {
@@ -735,7 +731,7 @@ export class SlashCommandHandler {
735 dstName = value[i + 2];731 dstName = value[i + 2];
736 i += 2;732 i += 2;
737 }733 }
738 const pick = candidates.find(it=>it.key == srcName);734 const pick = candidates.find(it => it.key == srcName);
739 if (!pick) throw new Error(`No scoped closure named "${srcName}" found in "${args.from}"`);735 if (!pick) throw new Error(`No scoped closure named "${srcName}" found in "${args.from}"`);
740 if (args._scope.existsVariableInScope(dstName)) {736 if (args._scope.existsVariableInScope(dstName)) {
741 args._scope.setVariable(dstName, pick.value);737 args._scope.setVariable(dstName, pick.value);
@@ -783,8 +779,6 @@ export class SlashCommandHandler {
783 }779 }
784780
785781
786
787
788 getSetByName(name) {782 getSetByName(name) {
789 const set = this.api.getSetByName(name);783 const set = this.api.getSetByName(name);
790 if (!set) {784 if (!set) {
@@ -802,8 +796,6 @@ export class SlashCommandHandler {
802 }796 }
803797
804798
805
806
807 async executeQuickReplyByIndex(idx) {799 async executeQuickReplyByIndex(idx) {
808 try {800 try {
809 return await this.api.executeQuickReplyByIndex(idx);801 return await this.api.executeQuickReplyByIndex(idx);
public/scripts/extensions/quick-reply/src/ui/ButtonUi.js+6 -14
@@ -10,15 +10,11 @@ export class ButtonUi {
10 /**@type {HTMLElement}*/ popoutDom;10 /**@type {HTMLElement}*/ popoutDom;
1111
1212
13
14
15 constructor(/**@type {QuickReplySettings}*/settings) {13 constructor(/**@type {QuickReplySettings}*/settings) {
16 this.settings = settings;14 this.settings = settings;
17 }15 }
1816
1917
20
21
22 render() {18 render() {
23 if (this.settings.isPopout) {19 if (this.settings.isPopout) {
24 return this.renderPopout();20 return this.renderPopout();
@@ -57,8 +53,6 @@ export class ButtonUi {
57 }53 }
5854
5955
60
61
62 renderBar() {56 renderBar() {
63 if (!this.dom) {57 if (!this.dom) {
64 let buttonHolder;58 let buttonHolder;
@@ -75,7 +69,7 @@ export class ButtonUi {
75 popout.classList.add('menu_button');69 popout.classList.add('menu_button');
76 popout.classList.add('fa-solid');70 popout.classList.add('fa-solid');
77 popout.classList.add('fa-window-restore');71 popout.classList.add('fa-window-restore');
78 popout.addEventListener('click', ()=>{72 popout.addEventListener('click', () => {
79 this.settings.isPopout = true;73 this.settings.isPopout = true;
80 this.refresh();74 this.refresh();
81 this.settings.save();75 this.settings.save();
@@ -91,8 +85,8 @@ export class ButtonUi {
91 }85 }
92 }86 }
93 [...this.settings.config.setList, ...(this.settings.chatConfig?.setList ?? []), ...(this.settings.charConfig?.setList ?? [])]87 [...this.settings.config.setList, ...(this.settings.chatConfig?.setList ?? []), ...(this.settings.charConfig?.setList ?? [])]
94 .filter(link=>link.isVisible)88 .filter(link => link.isVisible)
95 .forEach(link=>buttonHolder.append(link.set.render()))89 .forEach(link => buttonHolder.append(link.set.render()))
96 ;90 ;
97 }91 }
98 }92 }
@@ -100,8 +94,6 @@ export class ButtonUi {
100 }94 }
10195
10296
103
104
105 renderPopout() {97 renderPopout() {
106 if (!this.popoutDom) {98 if (!this.popoutDom) {
107 let buttonHolder;99 let buttonHolder;
@@ -130,7 +122,7 @@ export class ButtonUi {
130 close.classList.add('fa-solid');122 close.classList.add('fa-solid');
131 close.classList.add('fa-circle-xmark');123 close.classList.add('fa-circle-xmark');
132 close.classList.add('hoverglow');124 close.classList.add('hoverglow');
133 close.addEventListener('click', ()=>{125 close.addEventListener('click', () => {
134 this.settings.isPopout = false;126 this.settings.isPopout = false;
135 this.refresh();127 this.refresh();
136 this.settings.save();128 this.settings.save();
@@ -151,8 +143,8 @@ export class ButtonUi {
151 }143 }
152 }144 }
153 [...this.settings.config.setList, ...(this.settings.chatConfig?.setList ?? []), ...(this.settings.charConfig?.setList ?? [])]145 [...this.settings.config.setList, ...(this.settings.chatConfig?.setList ?? []), ...(this.settings.charConfig?.setList ?? [])]
154 .filter(link=>link.isVisible)146 .filter(link => link.isVisible)
155 .forEach(link=>buttonHolder.append(link.set.render()))147 .forEach(link => buttonHolder.append(link.set.render()))
156 ;148 ;
157 root.append(body);149 root.append(body);
158 }150 }
public/scripts/extensions/quick-reply/src/ui/SettingsUi.js+30 -38
@@ -29,24 +29,18 @@ export class SettingsUi {
29 /**@type {HTMLSelectElement}*/ currentSet;29 /**@type {HTMLSelectElement}*/ currentSet;
3030
3131
32
33
34 constructor(/**@type {QuickReplySettings}*/settings) {32 constructor(/**@type {QuickReplySettings}*/settings) {
35 this.settings = settings;33 this.settings = settings;
36 settings.onRequestEditSet = (qrs) => this.selectQrSet(qrs);34 settings.onRequestEditSet = (qrs) => this.selectQrSet(qrs);
37 }35 }
3836
3937
40
41
42
43
44 rerender() {38 rerender() {
45 if (!this.dom) return;39 if (!this.dom) return;
46 const content = this.dom.querySelector('.inline-drawer-content');40 const content = this.dom.querySelector('.inline-drawer-content');
47 content.innerHTML = '';41 content.innerHTML = '';
48 // @ts-ignore42 // @ts-ignore
49 Array.from(this.template.querySelector('.inline-drawer-content').cloneNode(true).children).forEach(el=>{43 Array.from(this.template.querySelector('.inline-drawer-content').cloneNode(true).children).forEach(el => {
50 content.append(el);44 content.append(el);
51 });45 });
52 this.prepareDom();46 this.prepareDom();
@@ -75,15 +69,15 @@ export class SettingsUi {
75 // general settings69 // general settings
76 this.isEnabled = this.dom.querySelector('#qr--isEnabled');70 this.isEnabled = this.dom.querySelector('#qr--isEnabled');
77 this.isEnabled.checked = this.settings.isEnabled;71 this.isEnabled.checked = this.settings.isEnabled;
78 this.isEnabled.addEventListener('click', ()=>this.onIsEnabled());72 this.isEnabled.addEventListener('click', () => this.onIsEnabled());
7973
80 this.isCombined = this.dom.querySelector('#qr--isCombined');74 this.isCombined = this.dom.querySelector('#qr--isCombined');
81 this.isCombined.checked = this.settings.isCombined;75 this.isCombined.checked = this.settings.isCombined;
82 this.isCombined.addEventListener('click', ()=>this.onIsCombined());76 this.isCombined.addEventListener('click', () => this.onIsCombined());
8377
84 this.showPopoutButton = this.dom.querySelector('#qr--showPopoutButton');78 this.showPopoutButton = this.dom.querySelector('#qr--showPopoutButton');
85 this.showPopoutButton.checked = this.settings.showPopoutButton;79 this.showPopoutButton.checked = this.settings.showPopoutButton;
86 this.showPopoutButton.addEventListener('click', ()=>this.onShowPopoutButton());80 this.showPopoutButton.addEventListener('click', () => this.onShowPopoutButton());
87 }81 }
8882
89 prepareGlobalSetList() {83 prepareGlobalSetList() {
@@ -131,29 +125,29 @@ export class SettingsUi {
131 prepareQrEditor() {125 prepareQrEditor() {
132 // qr editor126 // qr editor
133 this.dom.querySelector('#qr--set-rename').addEventListener('click', async () => this.renameQrSet());127 this.dom.querySelector('#qr--set-rename').addEventListener('click', async () => this.renameQrSet());
134 this.dom.querySelector('#qr--set-new').addEventListener('click', async()=>this.addQrSet());128 this.dom.querySelector('#qr--set-new').addEventListener('click', async () => this.addQrSet());
135 /**@type {HTMLInputElement}*/129 /**@type {HTMLInputElement}*/
136 const importFile = this.dom.querySelector('#qr--set-importFile');130 const importFile = this.dom.querySelector('#qr--set-importFile');
137 importFile.addEventListener('change', async()=>{131 importFile.addEventListener('change', async () => {
138 await this.importQrSet(importFile.files);132 await this.importQrSet(importFile.files);
139 importFile.value = null;133 importFile.value = null;
140 });134 });
141 this.dom.querySelector('#qr--set-import').addEventListener('click', ()=>importFile.click());135 this.dom.querySelector('#qr--set-import').addEventListener('click', () => importFile.click());
142 this.dom.querySelector('#qr--set-export').addEventListener('click', async () => this.exportQrSet());136 this.dom.querySelector('#qr--set-export').addEventListener('click', async () => this.exportQrSet());
143 this.dom.querySelector('#qr--set-duplicate').addEventListener('click', async () => this.duplicateQrSet());137 this.dom.querySelector('#qr--set-duplicate').addEventListener('click', async () => this.duplicateQrSet());
144 this.dom.querySelector('#qr--set-delete').addEventListener('click', async()=>this.deleteQrSet());138 this.dom.querySelector('#qr--set-delete').addEventListener('click', async () => this.deleteQrSet());
145 this.dom.querySelector('#qr--set-add').addEventListener('click', async()=>{139 this.dom.querySelector('#qr--set-add').addEventListener('click', async () => {
146 this.currentQrSet.addQuickReply();140 this.currentQrSet.addQuickReply();
147 });141 });
148 this.dom.querySelector('#qr--set-paste').addEventListener('click', async()=>{142 this.dom.querySelector('#qr--set-paste').addEventListener('click', async () => {
149 const text = await navigator.clipboard.readText();143 const text = await navigator.clipboard.readText();
150 this.currentQrSet.addQuickReplyFromText(text);144 this.currentQrSet.addQuickReplyFromText(text);
151 });145 });
152 this.dom.querySelector('#qr--set-importQr').addEventListener('click', async()=>{146 this.dom.querySelector('#qr--set-importQr').addEventListener('click', async () => {
153 const inp = document.createElement('input'); {147 const inp = document.createElement('input'); {
154 inp.type = 'file';148 inp.type = 'file';
155 inp.accept = '.json';149 inp.accept = '.json';
156 inp.addEventListener('change', async()=>{150 inp.addEventListener('change', async () => {
157 if (inp.files.length > 0) {151 if (inp.files.length > 0) {
158 for (const file of inp.files) {152 for (const file of inp.files) {
159 const text = await file.text();153 const text = await file.text();
@@ -166,8 +160,8 @@ export class SettingsUi {
166 });160 });
167 this.qrList = this.dom.querySelector('#qr--set-qrList');161 this.qrList = this.dom.querySelector('#qr--set-qrList');
168 this.currentSet = this.dom.querySelector('#qr--set');162 this.currentSet = this.dom.querySelector('#qr--set');
169 this.currentSet.addEventListener('change', ()=>this.onQrSetChange());163 this.currentSet.addEventListener('change', () => this.onQrSetChange());
170 QuickReplySet.list.toSorted((a,b)=>a.name.toLowerCase().localeCompare(b.name.toLowerCase())).forEach(qrs=>{164 QuickReplySet.list.toSorted((a, b) => a.name.toLowerCase().localeCompare(b.name.toLowerCase())).forEach(qrs => {
171 const opt = document.createElement('option'); {165 const opt = document.createElement('option'); {
172 opt.value = qrs.name;166 opt.value = qrs.name;
173 opt.textContent = qrs.name;167 opt.textContent = qrs.name;
@@ -175,19 +169,19 @@ export class SettingsUi {
175 }169 }
176 });170 });
177 this.disableSend = this.dom.querySelector('#qr--disableSend');171 this.disableSend = this.dom.querySelector('#qr--disableSend');
178 this.disableSend.addEventListener('click', ()=>{172 this.disableSend.addEventListener('click', () => {
179 const qrs = this.currentQrSet;173 const qrs = this.currentQrSet;
180 qrs.disableSend = this.disableSend.checked;174 qrs.disableSend = this.disableSend.checked;
181 qrs.save();175 qrs.save();
182 });176 });
183 this.placeBeforeInput = this.dom.querySelector('#qr--placeBeforeInput');177 this.placeBeforeInput = this.dom.querySelector('#qr--placeBeforeInput');
184 this.placeBeforeInput.addEventListener('click', ()=>{178 this.placeBeforeInput.addEventListener('click', () => {
185 const qrs = this.currentQrSet;179 const qrs = this.currentQrSet;
186 qrs.placeBeforeInput = this.placeBeforeInput.checked;180 qrs.placeBeforeInput = this.placeBeforeInput.checked;
187 qrs.save();181 qrs.save();
188 });182 });
189 this.injectInput = this.dom.querySelector('#qr--injectInput');183 this.injectInput = this.dom.querySelector('#qr--injectInput');
190 this.injectInput.addEventListener('click', ()=>{184 this.injectInput.addEventListener('click', () => {
191 const qrs = this.currentQrSet;185 const qrs = this.currentQrSet;
192 qrs.injectInput = this.injectInput.checked;186 qrs.injectInput = this.injectInput.checked;
193 qrs.save();187 qrs.save();
@@ -196,7 +190,7 @@ export class SettingsUi {
196 this.color = this.dom.querySelector('#qr--color');190 this.color = this.dom.querySelector('#qr--color');
197 // @ts-ignore191 // @ts-ignore
198 this.color.color = this.currentQrSet?.color ?? 'transparent';192 this.color.color = this.currentQrSet?.color ?? 'transparent';
199 this.color.addEventListener('change', (evt)=>{193 this.color.addEventListener('change', (evt) => {
200 if (!this.dom.closest('body')) return;194 if (!this.dom.closest('body')) return;
201 const qrs = this.currentQrSet;195 const qrs = this.currentQrSet;
202 if (initialColorChange) {196 if (initialColorChange) {
@@ -211,7 +205,7 @@ export class SettingsUi {
211 this.currentQrSet.updateColor();205 this.currentQrSet.updateColor();
212 });206 });
213 // @ts-ignore207 // @ts-ignore
214 this.dom.querySelector('#qr--colorClear').addEventListener('click', (evt)=>{208 this.dom.querySelector('#qr--colorClear').addEventListener('click', (evt) => {
215 const qrs = this.currentQrSet;209 const qrs = this.currentQrSet;
216 // @ts-ignore210 // @ts-ignore
217 this.color.color = 'transparent';211 this.color.color = 'transparent';
@@ -219,7 +213,7 @@ export class SettingsUi {
219 this.currentQrSet.updateColor();213 this.currentQrSet.updateColor();
220 });214 });
221 this.onlyBorderColor = this.dom.querySelector('#qr--onlyBorderColor');215 this.onlyBorderColor = this.dom.querySelector('#qr--onlyBorderColor');
222 this.onlyBorderColor.addEventListener('click', ()=>{216 this.onlyBorderColor.addEventListener('click', () => {
223 const qrs = this.currentQrSet;217 const qrs = this.currentQrSet;
224 qrs.onlyBorderColor = this.onlyBorderColor.checked;218 qrs.onlyBorderColor = this.onlyBorderColor.checked;
225 qrs.save();219 qrs.save();
@@ -242,7 +236,7 @@ export class SettingsUi {
242 $(qrsDom).sortable({236 $(qrsDom).sortable({
243 delay: getSortableDelay(),237 delay: getSortableDelay(),
244 handle: '.drag-handle',238 handle: '.drag-handle',
245 stop: ()=>this.onQrListSort(),239 stop: () => this.onQrListSort(),
246 });240 });
247 }241 }
248242
@@ -256,8 +250,6 @@ export class SettingsUi {
256 }250 }
257251
258252
259
260
261 async onIsEnabled() {253 async onIsEnabled() {
262 this.settings.isEnabled = this.isEnabled.checked;254 this.settings.isEnabled = this.isEnabled.checked;
263 this.settings.save();255 this.settings.save();
@@ -274,7 +266,7 @@ export class SettingsUi {
274 }266 }
275267
276 async onGlobalSetListSort() {268 async onGlobalSetListSort() {
277 this.settings.config.setList = Array.from(this.globalSetList.children).map((it,idx)=>{269 this.settings.config.setList = Array.from(this.globalSetList.children).map((it, idx) => {
278 const set = this.settings.config.setList[Number(it.getAttribute('data-order'))];270 const set = this.settings.config.setList[Number(it.getAttribute('data-order'))];
279 it.setAttribute('data-order', String(idx));271 it.setAttribute('data-order', String(idx));
280 return set;272 return set;
@@ -283,7 +275,7 @@ export class SettingsUi {
283 }275 }
284276
285 async onChatSetListSort() {277 async onChatSetListSort() {
286 this.settings.chatConfig.setList = Array.from(this.chatSetList.children).map((it,idx)=>{278 this.settings.chatConfig.setList = Array.from(this.chatSetList.children).map((it, idx) => {
287 const set = this.settings.chatConfig.setList[Number(it.getAttribute('data-order'))];279 const set = this.settings.chatConfig.setList[Number(it.getAttribute('data-order'))];
288 it.setAttribute('data-order', String(idx));280 it.setAttribute('data-order', String(idx));
289 return set;281 return set;
@@ -292,14 +284,14 @@ export class SettingsUi {
292 }284 }
293285
294 updateOrder(list) {286 updateOrder(list) {
295 Array.from(list.children).forEach((it,idx)=>{287 Array.from(list.children).forEach((it, idx) => {
296 it.setAttribute('data-order', idx);288 it.setAttribute('data-order', idx);
297 });289 });
298 }290 }
299291
300 async onQrListSort() {292 async onQrListSort() {
301 this.currentQrSet.qrList = Array.from(this.qrList.querySelectorAll('.qr--set-item')).map((it,idx)=>{293 this.currentQrSet.qrList = Array.from(this.qrList.querySelectorAll('.qr--set-item')).map((it, idx) => {
302 const qr = this.currentQrSet.qrList.find(qr=>qr.id == Number(it.getAttribute('data-id')));294 const qr = this.currentQrSet.qrList.find(qr => qr.id == Number(it.getAttribute('data-id')));
303 it.setAttribute('data-order', String(idx));295 it.setAttribute('data-order', String(idx));
304 return qr;296 return qr;
305 });297 });
@@ -408,7 +400,7 @@ export class SettingsUi {
408 const qrs = new QuickReplySet();400 const qrs = new QuickReplySet();
409 qrs.name = name;401 qrs.name = name;
410 qrs.addQuickReply();402 qrs.addQuickReply();
411 const idx = QuickReplySet.list.findIndex(it=>it.name.toLowerCase().localeCompare(name.toLowerCase()) == 1);403 const idx = QuickReplySet.list.findIndex(it => it.name.toLowerCase().localeCompare(name.toLowerCase()) == 1);
412 if (idx > -1) {404 if (idx > -1) {
413 QuickReplySet.list.splice(idx, 0, qrs);405 QuickReplySet.list.splice(idx, 0, qrs);
414 } else {406 } else {
@@ -448,7 +440,7 @@ export class SettingsUi {
448 } else {440 } else {
449 /**@type {QuickReplySet}*/441 /**@type {QuickReplySet}*/
450 const qrs = QuickReplySet.from(JSON.parse(JSON.stringify(props)));442 const qrs = QuickReplySet.from(JSON.parse(JSON.stringify(props)));
451 qrs.qrList = props.qrList.map(it=>QuickReply.from(it));443 qrs.qrList = props.qrList.map(it => QuickReply.from(it));
452 qrs.init();444 qrs.init();
453 const oldQrs = QuickReplySet.get(props.name);445 const oldQrs = QuickReplySet.get(props.name);
454 if (oldQrs) {446 if (oldQrs) {
@@ -466,7 +458,7 @@ export class SettingsUi {
466 this.prepareCharacterSetList();458 this.prepareCharacterSetList();
467 }459 }
468 } else {460 } else {
469 const idx = QuickReplySet.list.findIndex(it=>it.name.toLowerCase().localeCompare(qrs.name.toLowerCase()) == 1);461 const idx = QuickReplySet.list.findIndex(it => it.name.toLowerCase().localeCompare(qrs.name.toLowerCase()) == 1);
470 if (idx > -1) {462 if (idx > -1) {
471 QuickReplySet.list.splice(idx, 0, qrs);463 QuickReplySet.list.splice(idx, 0, qrs);
472 } else {464 } else {
@@ -496,7 +488,7 @@ export class SettingsUi {
496 }488 }
497489
498 exportQrSet() {490 exportQrSet() {
499 const blob = new Blob([JSON.stringify(this.currentQrSet)], { type:'application/json' });491 const blob = new Blob([JSON.stringify(this.currentQrSet)], { type: 'application/json' });
500 const url = URL.createObjectURL(blob);492 const url = URL.createObjectURL(blob);
501 const a = document.createElement('a'); {493 const a = document.createElement('a'); {
502 a.href = url;494 a.href = url;
public/scripts/extensions/quick-reply/src/ui/ctx/ContextMenu.js+0 -4
@@ -11,8 +11,6 @@ export class ContextMenu {
11 /**@type {HTMLElement}*/ menu;11 /**@type {HTMLElement}*/ menu;
1212
1313
14
15
16 constructor(/**@type {QuickReply}*/qr) {14 constructor(/**@type {QuickReply}*/qr) {
17 // this.itemList = items;15 // this.itemList = items;
18 this.itemList = this.build(qr).children;16 this.itemList = this.build(qr).children;
@@ -104,8 +102,6 @@ export class ContextMenu {
104 }102 }
105103
106104
107
108
109 show({ clientX, clientY }) {105 show({ clientX, clientY }) {
110 if (this.isActive) return;106 if (this.isActive) return;
111 this.isActive = true;107 this.isActive = true;
public/scripts/extensions/quick-reply/src/ui/ctx/MenuItem.js+0 -3
@@ -15,8 +15,6 @@ export class MenuItem {
15 /**@type {function}*/ onExpand;15 /**@type {function}*/ onExpand;
1616
1717
18
19
20 /**18 /**
21 *19 *
22 * @param {?string} icon20 * @param {?string} icon
@@ -80,7 +78,6 @@ export class MenuItem {
80 }78 }
81 item.addEventListener('mouseover', () => sub.show(item));79 item.addEventListener('mouseover', () => sub.show(item));
82 item.addEventListener('mouseleave', () => sub.hide());80 item.addEventListener('mouseleave', () => sub.hide());
83
84 }81 }
85 }82 }
86 }83 }
public/scripts/extensions/quick-reply/src/ui/ctx/SubMenu.js+0 -4
@@ -9,8 +9,6 @@ export class SubMenu {
9 /**@type {HTMLElement}*/ root;9 /**@type {HTMLElement}*/ root;
1010
1111
12
13
14 constructor(/**@type {MenuItem[]}*/items) {12 constructor(/**@type {MenuItem[]}*/items) {
15 this.itemList = items;13 this.itemList = items;
16 }14 }
@@ -29,8 +27,6 @@ export class SubMenu {
29 }27 }
3028
3129
32
33
34 show(/**@type {HTMLElement}*/parent) {30 show(/**@type {HTMLElement}*/parent) {
35 if (this.isActive) return;31 if (this.isActive) return;
36 this.isActive = true;32 this.isActive = true;
public/scripts/extensions/regex/index.js+0 -1
@@ -1031,7 +1031,6 @@ function executeRegexScriptForDebugging(script, text) {
1031 const trailingText = text.substring(lastIndex);1031 const trailingText = text.substring(lastIndex);
1032 outputText += trailingText;1032 outputText += trailingText;
1033 highlightedOutput += escapeHtml(trailingText);1033 highlightedOutput += escapeHtml(trailingText);
1034
1035 } catch (e) {1034 } catch (e) {
1036 err = (err ? err + '; ' : '') + `Replace error: ${e.message}`;1035 err = (err ? err + '; ' : '') + `Replace error: ${e.message}`;
1037 outputText = text; // Fallback1036 outputText = text; // Fallback
public/scripts/extensions/token-counter/index.js+0 -1
@@ -115,5 +115,4 @@ jQuery(() => {
115 returns: 'number of tokens',115 returns: 'number of tokens',
116 helpString: 'Counts the number of tokens in the current chat.',116 helpString: 'Counts the number of tokens in the current chat.',
117 }));117 }));
118
119});118});
public/scripts/extensions/tts/alltalk.js+0 -1
@@ -1043,7 +1043,6 @@ class AllTalkTtsProvider {
1043 // V2: Combine the endpoint with the relative path1043 // V2: Combine the endpoint with the relative path
1044 return `${this.settings.provider_endpoint}${data.output_file_url}`;1044 return `${this.settings.provider_endpoint}${data.output_file_url}`;
1045 }1045 }
1046
1047 } catch (error) {1046 } catch (error) {
1048 console.error('[fetchTtsGeneration] Exception caught:', error);1047 console.error('[fetchTtsGeneration] Exception caught:', error);
1049 throw error;1048 throw error;
public/scripts/extensions/tts/chatterbox.js+0 -3
@@ -239,7 +239,6 @@ class ChatterboxTtsProvider {
239 }239 }
240240
241 this.setupEventListeners();241 this.setupEventListeners();
242
243 } catch (error) {242 } catch (error) {
244 console.error('Error loading Chatterbox settings:', error);243 console.error('Error loading Chatterbox settings:', error);
245 this.updateStatus('Offline');244 this.updateStatus('Offline');
@@ -518,7 +517,6 @@ class ChatterboxTtsProvider {
518 });517 });
519518
520 await audio.play();519 await audio.play();
521
522 } catch (error) {520 } catch (error) {
523 console.error('Error previewing voice:', error);521 console.error('Error previewing voice:', error);
524 this.updateStatus('Ready');522 this.updateStatus('Ready');
@@ -627,7 +625,6 @@ class ChatterboxTtsProvider {
627625
628 // Return the response directly - SillyTavern expects a Response object626 // Return the response directly - SillyTavern expects a Response object
629 return response;627 return response;
630
631 } catch (error) {628 } catch (error) {
632 console.error('Error in generateTts:', error);629 console.error('Error in generateTts:', error);
633 this.updateStatus('Ready');630 this.updateStatus('Ready');
public/scripts/extensions/tts/coqui.js+12 -13
@@ -160,7 +160,7 @@ class CoquiTtsProvider {
160 .then(response => response.json())160 .then(response => response.json())
161 .then(json => {161 .then(json => {
162 coquiApiModels = json;162 coquiApiModels = json;
163 console.debug(DEBUG_PREFIX,'initialized coqui-api model list to', coquiApiModels);163 console.debug(DEBUG_PREFIX, 'initialized coqui-api model list to', coquiApiModels);
164 /*164 /*
165 $('#coqui_api_language')165 $('#coqui_api_language')
166 .find('option')166 .find('option')
@@ -180,7 +180,7 @@ class CoquiTtsProvider {
180 .then(response => response.json())180 .then(response => response.json())
181 .then(json => {181 .then(json => {
182 coquiApiModelsFull = json;182 coquiApiModelsFull = json;
183 console.debug(DEBUG_PREFIX,'initialized coqui-api full model list to', coquiApiModelsFull);183 console.debug(DEBUG_PREFIX, 'initialized coqui-api full model list to', coquiApiModelsFull);
184 /*184 /*
185 $('#coqui_api_full_language')185 $('#coqui_api_full_language')
186 .find('option')186 .find('option')
@@ -197,7 +197,7 @@ class CoquiTtsProvider {
197 }197 }
198198
199 // Perform a simple readiness check by trying to fetch voiceIds199 // Perform a simple readiness check by trying to fetch voiceIds
200 async checkReady(){200 async checkReady() {
201 throwIfModuleMissing();201 throwIfModuleMissing();
202 await this.fetchTtsVoiceObjects();202 await this.fetchTtsVoiceObjects();
203 }203 }
@@ -384,12 +384,12 @@ class CoquiTtsProvider {
384 .append('<option value="none">Select model language</option>')384 .append('<option value="none">Select model language</option>')
385 .val('none');385 .val('none');
386386
387 for(let language in coquiApiModels) {387 for (let language in coquiApiModels) {
388 let languageLabel = language;388 let languageLabel = language;
389 if (language in languageLabels)389 if (language in languageLabels)
390 languageLabel = languageLabels[language];390 languageLabel = languageLabels[language];
391 $('#coqui_api_language').append(new Option(languageLabel,language));391 $('#coqui_api_language').append(new Option(languageLabel, language));
392 console.log(DEBUG_PREFIX,'added language',languageLabel,'(',language,')');392 console.log(DEBUG_PREFIX, 'added language', languageLabel, '(', language, ')');
393 }393 }
394394
395 $('#coqui_api_model_div').show();395 $('#coqui_api_model_div').show();
@@ -406,12 +406,12 @@ class CoquiTtsProvider {
406 .append('<option value="none">Select model language</option>')406 .append('<option value="none">Select model language</option>')
407 .val('none');407 .val('none');
408408
409 for(let language in coquiApiModelsFull) {409 for (let language in coquiApiModelsFull) {
410 let languageLabel = language;410 let languageLabel = language;
411 if (language in languageLabels)411 if (language in languageLabels)
412 languageLabel = languageLabels[language];412 languageLabel = languageLabels[language];
413 $('#coqui_api_language').append(new Option(languageLabel,language));413 $('#coqui_api_language').append(new Option(languageLabel, language));
414 console.log(DEBUG_PREFIX,'added language',languageLabel,'(',language,')');414 console.log(DEBUG_PREFIX, 'added language', languageLabel, '(', language, ')');
415 }415 }
416416
417 $('#coqui_api_model_div').show();417 $('#coqui_api_model_div').show();
@@ -450,8 +450,8 @@ class CoquiTtsProvider {
450 if (model_origin == 'coqui-api-full')450 if (model_origin == 'coqui-api-full')
451 modelDict = coquiApiModelsFull;451 modelDict = coquiApiModelsFull;
452452
453 for(let model_dataset in modelDict[model_language])453 for (let model_dataset in modelDict[model_language])
454 for(let model_name in modelDict[model_language][model_dataset]) {454 for (let model_name in modelDict[model_language][model_dataset]) {
455 const model_id = model_dataset + '/' + model_name;455 const model_id = model_dataset + '/' + model_name;
456 const model_label = model_name + ' (' + model_dataset + ' dataset)';456 const model_label = model_name + ' (' + model_dataset + ' dataset)';
457 $('#coqui_api_model_name').append(new Option(model_label, model_id));457 $('#coqui_api_model_name').append(new Option(model_label, model_id));
@@ -526,7 +526,7 @@ class CoquiTtsProvider {
526526
527 // Check if already installed and propose to do it otherwise527 // Check if already installed and propose to do it otherwise
528 const model_id = modelDict[model_language][model_dataset][model_name].id;528 const model_id = modelDict[model_language][model_dataset][model_name].id;
529 console.debug(DEBUG_PREFIX,'Check if model is already installed',model_id);529 console.debug(DEBUG_PREFIX, 'Check if model is already installed', model_id);
530 const result = await CoquiTtsProvider.checkmodel_state(model_id);530 const result = await CoquiTtsProvider.checkmodel_state(model_id);
531 const resultJSON = await result.json();531 const resultJSON = await result.json();
532 const model_state = resultJSON.model_state;532 const model_state = resultJSON.model_state;
@@ -583,7 +583,6 @@ class CoquiTtsProvider {
583 $('#coqui_api_model_install_button').show();583 $('#coqui_api_model_install_button').show();
584 return;584 return;
585 }585 }
586
587 }586 }
588587
589588
public/scripts/extensions/tts/cosyvoice.js+0 -6
@@ -110,15 +110,11 @@ class CosyVoiceProvider {
110 //#################//110 //#################//
111111
112 async getVoice(voiceName) {112 async getVoice(voiceName) {
113
114
115
116 if (this.voices.length == 0) {113 if (this.voices.length == 0) {
117 this.voices = await this.fetchTtsVoiceObjects();114 this.voices = await this.fetchTtsVoiceObjects();
118 }115 }
119116
120117
121
122 const match = this.voices.filter(118 const match = this.voices.filter(
123 v => v.name == voiceName,119 v => v.name == voiceName,
124 )[0];120 )[0];
@@ -130,7 +126,6 @@ class CosyVoiceProvider {
130 }126 }
131127
132128
133
134 async generateTts(text, voiceId) {129 async generateTts(text, voiceId) {
135 const response = await this.fetchTtsGeneration(text, voiceId);130 const response = await this.fetchTtsGeneration(text, voiceId);
136 return response;131 return response;
@@ -198,7 +193,6 @@ class CosyVoiceProvider {
198 }193 }
199194
200195
201
202 // Interface not used196 // Interface not used
203 async fetchTtsFromHistory(history_item_id) {197 async fetchTtsFromHistory(history_item_id) {
204 return Promise.resolve(history_item_id);198 return Promise.resolve(history_item_id);
public/scripts/extensions/tts/elevenlabs.js+1 -1
@@ -132,7 +132,7 @@ class ElevenLabsTtsProvider {
132 }132 }
133133
134 if (Object.hasOwn(settings, 'apiKey')) {134 if (Object.hasOwn(settings, 'apiKey')) {
135 if (settings.apiKey && !secret_state[SECRET_KEYS.ELEVENLABS]){135 if (settings.apiKey && !secret_state[SECRET_KEYS.ELEVENLABS]) {
136 await writeSecret(SECRET_KEYS.ELEVENLABS, settings.apiKey);136 await writeSecret(SECRET_KEYS.ELEVENLABS, settings.apiKey);
137 }137 }
138 delete settings.apiKey;138 delete settings.apiKey;
public/scripts/extensions/tts/google-native.js+0 -2
@@ -124,7 +124,6 @@ export class GoogleNativeTtsProvider {
124 console.info(`Google TTS: Loaded ${this.voices.length} voices`);124 console.info(`Google TTS: Loaded ${this.voices.length} voices`);
125125
126 return this.voices;126 return this.voices;
127
128 } catch (error) {127 } catch (error) {
129 console.error('Failed to fetch Google TTS voices:', error);128 console.error('Failed to fetch Google TTS voices:', error);
130 throw error;129 throw error;
@@ -151,7 +150,6 @@ export class GoogleNativeTtsProvider {
151 this.audioElement.src = url;150 this.audioElement.src = url;
152 this.audioElement.play();151 this.audioElement.play();
153 this.audioElement.onended = () => URL.revokeObjectURL(url);152 this.audioElement.onended = () => URL.revokeObjectURL(url);
154
155 } catch (error) {153 } catch (error) {
156 console.error('TTS Preview Error:', error);154 console.error('TTS Preview Error:', error);
157 toastr.error(`Could not generate preview: ${error.message}`);155 toastr.error(`Could not generate preview: ${error.message}`);
public/scripts/extensions/tts/gpt-sovits-v2.js+0 -8
@@ -115,15 +115,11 @@ class GptSovitsV2Provider {
115 //#################//115 //#################//
116116
117 async getVoice(voiceName) {117 async getVoice(voiceName) {
118
119
120
121 if (this.voices.length == 0) {118 if (this.voices.length == 0) {
122 this.voices = await this.fetchTtsVoiceObjects();119 this.voices = await this.fetchTtsVoiceObjects();
123 }120 }
124121
125122
126
127 const match = this.voices.filter(123 const match = this.voices.filter(
128 v => v.name == voiceName,124 v => v.name == voiceName,
129 )[0];125 )[0];
@@ -135,7 +131,6 @@ class GptSovitsV2Provider {
135 }131 }
136132
137133
138
139 async generateTts(text, voiceId) {134 async generateTts(text, voiceId) {
140 const response = await this.fetchTtsGeneration(text, voiceId);135 const response = await this.fetchTtsGeneration(text, voiceId);
141 return response;136 return response;
@@ -171,8 +166,6 @@ class GptSovitsV2Provider {
171 */166 */
172167
173168
174
175
176 async fetchTtsGeneration(inputText, voiceId, lang = null, forceNoStreaming = false) {169 async fetchTtsGeneration(inputText, voiceId, lang = null, forceNoStreaming = false) {
177 console.info(`Generating new TTS for voice_id ${voiceId}`);170 console.info(`Generating new TTS for voice_id ${voiceId}`);
178171
@@ -215,7 +208,6 @@ class GptSovitsV2Provider {
215 }208 }
216209
217210
218
219 // Interface not used211 // Interface not used
220 async fetchTtsFromHistory(history_item_id) {212 async fetchTtsFromHistory(history_item_id) {
221 return Promise.resolve(history_item_id);213 return Promise.resolve(history_item_id);
public/scripts/extensions/tts/gsvi.js+0 -9
@@ -1,4 +1,3 @@
1
2import { saveTtsProviderSettings } from './index.js';1import { saveTtsProviderSettings } from './index.js';
32
4export { GSVITtsProvider };3export { GSVITtsProvider };
@@ -60,11 +59,9 @@ class GSVITtsProvider {
60 const characterList = await response.json();59 const characterList = await response.json();
61 this.characterList = characterList;60 this.characterList = characterList;
62 this.voices = Object.keys(characterList);61 this.voices = Object.keys(characterList);
63
64 }62 }
6563
6664
67
68 get settingsHtml() {65 get settingsHtml() {
69 let html = `66 let html = `
70 <label for="gsvi_api_language">Text Language</label>67 <label for="gsvi_api_language">Text Language</label>
@@ -142,11 +139,8 @@ class GSVITtsProvider {
142 $('#gsvi_batch_size_output').text(this.settings.batch_size);139 $('#gsvi_batch_size_output').text(this.settings.batch_size);
143140
144141
145
146
147 // Persist settings changes142 // Persist settings changes
148 saveTtsProviderSettings();143 saveTtsProviderSettings();
149
150 }144 }
151145
152 async loadSettings(settings) {146 async loadSettings(settings) {
@@ -197,7 +191,6 @@ class GSVITtsProvider {
197 }191 }
198192
199193
200
201 // Perform a simple readiness check by trying to fetch voiceIds194 // Perform a simple readiness check by trying to fetch voiceIds
202 async checkReady() {195 async checkReady() {
203 await Promise.allSettled([this.fetchCharacterList()]);196 await Promise.allSettled([this.fetchCharacterList()]);
@@ -256,12 +249,10 @@ class GSVITtsProvider {
256249
257250
258 return `${this.settings.provider_endpoint}/tts?${params.toString()}`;251 return `${this.settings.provider_endpoint}/tts?${params.toString()}`;
259
260 }252 }
261253
262 // Interface not used by GSVI TTS254 // Interface not used by GSVI TTS
263 async fetchTtsFromHistory(history_item_id) {255 async fetchTtsFromHistory(history_item_id) {
264 return Promise.resolve(history_item_id);256 return Promise.resolve(history_item_id);
265 }257 }
266
267}258}
public/scripts/extensions/tts/index.js+0 -4
@@ -611,7 +611,6 @@ async function processTtsQueue() {
611611
612 // Pass the full voiceMapKey (e.g., "User ("Quotes")") as well with character name612 // Pass the full voiceMapKey (e.g., "User ("Quotes")") as well with character name
613 await tts(segmentText, voiceId, char, voiceMapKey);613 await tts(segmentText, voiceId, char, voiceMapKey);
614
615 } catch (error) {614 } catch (error) {
616 toastr.error(error.toString());615 toastr.error(error.toString());
617 console.error(error);616 console.error(error);
@@ -707,7 +706,6 @@ async function processTtsQueue() {
707706
708 // Clear current job so the segmented jobs can be processed707 // Clear current job so the segmented jobs can be processed
709 currentTtsJob = null;708 currentTtsJob = null;
710
711 } catch (error) {709 } catch (error) {
712 toastr.error(error.toString());710 toastr.error(error.toString());
713 console.error(error);711 console.error(error);
@@ -1286,7 +1284,6 @@ export function getCharacters(unrestricted) {
1286 }1284 }
12871285
1288 return characters;1286 return characters;
1289
1290}1287}
12911288
1292export function sanitizeId(input) {1289export function sanitizeId(input) {
@@ -1314,7 +1311,6 @@ function parseVoiceMap(voiceMapString) {
1314}1311}
13151312
13161313
1317
1318/**1314/**
1319 * Apply voiceMap based on current voiceMapEntries1315 * Apply voiceMap based on current voiceMapEntries
1320 */1316 */
public/scripts/extensions/tts/kokoro-worker.js+1 -1
@@ -7,7 +7,7 @@ let ready = false;
7let voices = [];7let voices = [];
88
9// Handle messages from the main thread9// Handle messages from the main thread
10self.onmessage = async function(e) {10self.onmessage = async function (e) {
11 const { action, data } = e.data;11 const { action, data } = e.data;
1212
13 switch (action) {13 switch (action) {
public/scripts/extensions/tts/minimax.js+0 -2
@@ -837,7 +837,6 @@ class MiniMaxTtsProvider {
837 // Backend handles all the complex processing and returns audio data directly837 // Backend handles all the complex processing and returns audio data directly
838 console.debug('MiniMax TTS: Audio response received from backend');838 console.debug('MiniMax TTS: Audio response received from backend');
839 return response;839 return response;
840
841 } catch (error) {840 } catch (error) {
842 console.error('Error in MiniMax TTS generation:', error);841 console.error('Error in MiniMax TTS generation:', error);
843 throw error;842 throw error;
@@ -954,7 +953,6 @@ class MiniMaxTtsProvider {
954 this.audioElement.onended = null;953 this.audioElement.onended = null;
955 this.audioElement.onerror = null;954 this.audioElement.onerror = null;
956 };955 };
957
958 } catch (error) {956 } catch (error) {
959 console.error('MiniMax TTS Preview Error:', error);957 console.error('MiniMax TTS Preview Error:', error);
960 toastr.error(`Could not generate preview: ${error.message}`);958 toastr.error(`Could not generate preview: ${error.message}`);
public/scripts/extensions/tts/openai.js+0 -1
@@ -146,7 +146,6 @@ class OpenAITtsProvider {
146 }146 }
147147
148 populateCharacterInstructions() {148 populateCharacterInstructions() {
149
150 const currentCharacters = $('.tts_voicemap_block_char span').map((i, el) => $(el).text()).get();149 const currentCharacters = $('.tts_voicemap_block_char span').map((i, el) => $(el).text()).get();
151150
152 $('#openai-character-instructions').empty();151 $('#openai-character-instructions').empty();
public/scripts/extensions/tts/silerotts.js+0 -1
@@ -172,5 +172,4 @@ class SileroTtsProvider {
172 async fetchTtsFromHistory(history_item_id) {172 async fetchTtsFromHistory(history_item_id) {
173 return Promise.resolve(history_item_id);173 return Promise.resolve(history_item_id);
174 }174 }
175
176}175}
public/scripts/extensions/tts/xtts.js+0 -1
@@ -323,5 +323,4 @@ class XTTSTtsProvider {
323 async fetchTtsFromHistory(history_item_id) {323 async fetchTtsFromHistory(history_item_id) {
324 return Promise.resolve(history_item_id);324 return Promise.resolve(history_item_id);
325 }325 }
326
327}326}
public/scripts/extensions/vectors/index.js+0 -1
@@ -1456,7 +1456,6 @@ async function onViewStatsClick() {
1456 messageElement.addClass('vectorized');1456 messageElement.addClass('vectorized');
1457 }1457 }
1458 }1458 }
1459
1460}1459}
14611460
1462async function onVectorizeAllFilesClick() {1461async function onVectorizeAllFilesClick() {
public/scripts/f-localStorage.js+0 -1
@@ -13,7 +13,6 @@ export function SaveLocal(target, val) {
13export function LoadLocal(target) {13export function LoadLocal(target) {
14 console.debug('LoadLocal -- ' + target);14 console.debug('LoadLocal -- ' + target);
15 return localStorage.getItem(target);15 return localStorage.getItem(target);
16
17}16}
18/**17/**
19 * @deprecated THIS FUNCTION IS OBSOLETE. DO NOT USE18 * @deprecated THIS FUNCTION IS OBSOLETE. DO NOT USE
public/scripts/filters.js+0 -1
@@ -76,7 +76,6 @@ export const fuzzySearchCategories = Object.freeze({
76 * data = filterHelper.applyFilters(data);76 * data = filterHelper.applyFilters(data);
77 */77 */
78export class FilterHelper {78export class FilterHelper {
79
80 /**79 /**
81 * Cache fuzzy search weighting scores for re-usability, sorting and stuff80 * Cache fuzzy search weighting scores for re-usability, sorting and stuff
82 *81 *
public/scripts/group-chats.js+0 -1
@@ -2477,7 +2477,6 @@ jQuery(() => {
2477 const value = $(this).prop('checked');2477 const value = $(this).prop('checked');
2478 hideMutedSprites = value;2478 hideMutedSprites = value;
2479 onHideMutedSpritesClick(value);2479 onHideMutedSpritesClick(value);
2480
2481 });2480 });
2482 $('#send_textarea').on('keyup', onSendTextareaInput);2481 $('#send_textarea').on('keyup', onSendTextareaInput);
2483 $('#groupCurrentMemberPopoutButton').on('click', doCurMemberListPopout);2482 $('#groupCurrentMemberPopoutButton').on('click', doCurMemberListPopout);
public/scripts/input-md-formatting.js+4 -5
@@ -59,7 +59,8 @@ export function initInputMarkdown() {
59 let cursorShift = charsToAdd.length;59 let cursorShift = charsToAdd.length;
60 let selectedTextandPossibleFormatting = textarea.value.substring(start - possiblePreviousFormattingMargin, end + possiblePreviousFormattingMargin).trim();60 let selectedTextandPossibleFormatting = textarea.value.substring(start - possiblePreviousFormattingMargin, end + possiblePreviousFormattingMargin).trim();
6161
62 if (isTextSelected) { //if text is selected62 if (isTextSelected) {
63 //if text is selected
63 selectedText = textarea.value.substring(start, end);64 selectedText = textarea.value.substring(start, end);
64 if (selectedTextandPossibleFormatting === charsToAdd + selectedText + charsToAdd) {65 if (selectedTextandPossibleFormatting === charsToAdd + selectedText + charsToAdd) {
65 // If the selected text is already formatted, remove the formatting66 // If the selected text is already formatted, remove the formatting
@@ -90,7 +91,8 @@ export function initInputMarkdown() {
90 textarea.focus();91 textarea.focus();
91 document.execCommand('insertText', false, charsToAdd + selectedText + charsToAdd + possibleAddedSpace);92 document.execCommand('insertText', false, charsToAdd + selectedText + charsToAdd + possibleAddedSpace);
92 }93 }
93 } else {// No text is selected94 } else {
95 // No text is selected
94 //check 1 character before and after the cursor for non-space characters96 //check 1 character before and after the cursor for non-space characters
9597
96 if (beforeCaret !== ' ' && afterCaret !== ' ' && afterCaret !== '' && beforeCaret !== '') { //look for caret in the middle of a word98 if (beforeCaret !== ' ' && afterCaret !== ' ' && afterCaret !== '' && beforeCaret !== '') { //look for caret in the middle of a word
@@ -116,7 +118,6 @@ export function initInputMarkdown() {
116 }118 }
117119
118 if (charsToAdd + discoveredWord + charsToAdd === discoveredWordWithPossibleFormatting) {120 if (charsToAdd + discoveredWord + charsToAdd === discoveredWordWithPossibleFormatting) {
119
120 // Replace the expanded selection with the original discovered word121 // Replace the expanded selection with the original discovered word
121 textarea.focus();122 textarea.focus();
122 document.execCommand('insertText', false, discoveredWord);123 document.execCommand('insertText', false, discoveredWord);
@@ -126,8 +127,6 @@ export function initInputMarkdown() {
126 textarea.focus();127 textarea.focus();
127 document.execCommand('insertText', false, charsToAdd + discoveredWord + charsToAdd);128 document.execCommand('insertText', false, charsToAdd + discoveredWord + charsToAdd);
128 }129 }
129
130
131 } else { //caret is not inside a word, so just add the formatting130 } else { //caret is not inside a word, so just add the formatting
132 textarea.focus();131 textarea.focus();
133 textarea.setSelectionRange(start, end);132 textarea.setSelectionRange(start, end);
public/scripts/instruct-mode.js+0 -1
@@ -798,7 +798,6 @@ jQuery(() => {
798 $('#instruct_system_sequence').prop('readOnly', false);798 $('#instruct_system_sequence').prop('readOnly', false);
799 $('#instruct_system_suffix').prop('readOnly', false);799 $('#instruct_system_suffix').prop('readOnly', false);
800 }800 }
801
802 });801 });
803802
804 $('#instruct_enabled').on('change', function () {803 $('#instruct_enabled').on('change', function () {
public/scripts/openai.js+0 -4
@@ -2279,7 +2279,6 @@ function appendElectronHubOptions(model_list, groupModels = false) {
2279 appendOption(model);2279 appendOption(model);
2280 });2280 });
2281 }2281 }
2282
2283}2282}
22842283
2285function electronHubSortBy(data, property = 'alphabetically') {2284function electronHubSortBy(data, property = 'alphabetically') {
@@ -3985,7 +3984,6 @@ function loadOpenAISettings(data, settings) {
3985 option.value = i;3984 option.value = i;
3986 option.text = item;3985 option.text = item;
3987 $('#settings_preset_openai').append(option);3986 $('#settings_preset_openai').append(option);
3988
3989 });3987 });
3990 openai_setting_names = settingNames;3988 openai_setting_names = settingNames;
39913989
@@ -4896,7 +4894,6 @@ function getSiliconflowMaxContext(model, isUnlocked) {
48964894
4897 // Return context size if model found, otherwise default to 32k4895 // Return context size if model found, otherwise default to 32k
4898 return Object.entries(contextMap).find(([key]) => model.includes(key))?.[1] || max_32k;4896 return Object.entries(contextMap).find(([key]) => model.includes(key))?.[1] || max_32k;
4899
4900}4897}
49014898
4902/**4899/**
@@ -5041,7 +5038,6 @@ async function onModelChange() {
5041 console.log('Claude model changed to', value);5038 console.log('Claude model changed to', value);
5042 oai_settings.claude_model = value;5039 oai_settings.claude_model = value;
5043 $('#model_claude_select').val(oai_settings.claude_model);5040 $('#model_claude_select').val(oai_settings.claude_model);
5044
5045 }5041 }
50465042
5047 if ($(this).is('#model_openai_select')) {5043 if ($(this).is('#model_openai_select')) {
public/scripts/personas.js+0 -2
@@ -1593,7 +1593,6 @@ export async function showCharConnections() {
1593 highlightPersonas: true,1593 highlightPersonas: true,
1594 targetedChar: getCurrentConnectionObj(),1594 targetedChar: getCurrentConnectionObj(),
1595 shiftClickHandler: (element, ev) => {1595 shiftClickHandler: (element, ev) => {
1596
1597 const personaId = $(element).attr('data-pid');1596 const personaId = $(element).attr('data-pid');
15981597
1599 /** @type {PersonaConnection[]} */1598 /** @type {PersonaConnection[]} */
@@ -1845,7 +1844,6 @@ async function lockPersonaCallback(_args, value) {
1845 if (isFalseBoolean(value)) {1844 if (isFalseBoolean(value)) {
1846 await setPersonaLockState(false, type);1845 await setPersonaLockState(false, type);
1847 return 'false';1846 return 'false';
1848
1849 }1847 }
18501848
1851 return '';1849 return '';
public/scripts/popup.js+0 -1
@@ -478,7 +478,6 @@ export class Popup {
478 break;478 break;
479 }479 }
480 }480 }
481
482 };481 };
483 this.dlg.addEventListener('keydown', keyListener.bind(this));482 this.dlg.addEventListener('keydown', keyListener.bind(this));
484 }483 }
public/scripts/power-user.js+1 -8
@@ -536,7 +536,6 @@ function switchSwipeNumAllMessages() {
536var originalSliderValues = [];536var originalSliderValues = [];
537537
538async function switchLabMode({ noReset = false } = {}) {538async function switchLabMode({ noReset = false } = {}) {
539
540 /* if (power_user.enableZenSliders && power_user.enableLabMode) {539 /* if (power_user.enableZenSliders && power_user.enableLabMode) {
541 toastr.warning("Can't start Lab Mode while Zen Sliders are active")540 toastr.warning("Can't start Lab Mode while Zen Sliders are active")
542 return541 return
@@ -571,8 +570,6 @@ async function switchLabMode({ noReset = false } = {}) {
571 $('#amount_gen').attr('min', '1')570 $('#amount_gen').attr('min', '1')
572 .attr('max', '99999')571 .attr('max', '99999')
573 .attr('step', '1');572 .attr('step', '1');
574
575
576 } else if (!noReset) {573 } else if (!noReset) {
577 //re apply the original sliders values to each input574 //re apply the original sliders values to each input
578 originalSliderValues.forEach(function (slider) {575 originalSliderValues.forEach(function (slider) {
@@ -628,7 +625,6 @@ async function switchZenSliders() {
628 });625 });
629 $('div[id$="_zenslider"]').remove();626 $('div[id$="_zenslider"]').remove();
630 }627 }
631
632}628}
633async function CreateZenSliders(elmnt) {629async function CreateZenSliders(elmnt) {
634 var originalSlider = elmnt;630 var originalSlider = elmnt;
@@ -1178,7 +1174,6 @@ function applyShadowWidth() {
1178 document.documentElement.style.setProperty('--shadowWidth', String(power_user.shadow_width));1174 document.documentElement.style.setProperty('--shadowWidth', String(power_user.shadow_width));
1179 $('#shadow_width_counter').val(power_user.shadow_width);1175 $('#shadow_width_counter').val(power_user.shadow_width);
1180 $('#shadow_width').val(power_user.shadow_width);1176 $('#shadow_width').val(power_user.shadow_width);
1181
1182}1177}
11831178
1184function applyFontScale(type) {1179function applyFontScale(type) {
@@ -2954,7 +2949,6 @@ function setAvgBG() {
2954 } */2949 } */
29552950
2956 function getAverageRGB(imgEl) {2951 function getAverageRGB(imgEl) {
2957
2958 var blockSize = 5, // only visit every 5 pixels2952 var blockSize = 5, // only visit every 5 pixels
2959 defaultRGB = { r: 0, g: 0, b: 0 }, // for non-supporting envs2953 defaultRGB = { r: 0, g: 0, b: 0 }, // for non-supporting envs
2960 canvas = document.createElement('canvas'),2954 canvas = document.createElement('canvas'),
@@ -2994,7 +2988,6 @@ function setAvgBG() {
2994 rgb.b = ~~(rgb.b / count);2988 rgb.b = ~~(rgb.b / count);
29952989
2996 return rgb;2990 return rgb;
2997
2998 }2991 }
29992992
3000 /**2993 /**
@@ -4035,7 +4028,7 @@ jQuery(() => {
4035 return;4028 return;
4036 }4029 }
40374030
4038 eventSource.once(event_types.SETTINGS_UPDATED, function() {4031 eventSource.once(event_types.SETTINGS_UPDATED, function () {
4039 toastr.warning(4032 toastr.warning(
4040 t`Click here to reload.`,4033 t`Click here to reload.`,
4041 t`Toggling the Experimental Macro Engine requires a reload.`,4034 t`Toggling the Experimental Macro Engine requires a reload.`,
public/scripts/preset-manager.js+0 -1
@@ -514,7 +514,6 @@ class PresetManager {
514 console.error('Preset could not be renamed', error);514 console.error('Preset could not be renamed', error);
515 throw new Error('Preset could not be renamed');515 throw new Error('Preset could not be renamed');
516 }516 }
517
518 }517 }
519518
520 /**519 /**
public/scripts/reasoning.js+0 -1
@@ -865,7 +865,6 @@ function selectReasoningTemplateCallback(args, name) {
865 UI.$select.val(foundName).trigger('change');865 UI.$select.val(foundName).trigger('change');
866 !quiet && toastr.success(`Reasoning template "${foundName}" selected`);866 !quiet && toastr.success(`Reasoning template "${foundName}" selected`);
867 return foundName;867 return foundName;
868
869}868}
870869
871function registerReasoningSlashCommands() {870function registerReasoningSlashCommands() {
public/scripts/samplerSelect.js+0 -1
@@ -203,7 +203,6 @@ function setSamplerListListeners() {
203203
204 console.log(samplerName, relatedDOMElement.data(SELECT_SAMPLER.DATA), shouldDisplay);204 console.log(samplerName, relatedDOMElement.data(SELECT_SAMPLER.DATA), shouldDisplay);
205 });205 });
206
207}206}
208207
209function isElementVisibleInDOM(element) {208function isElementVisibleInDOM(element) {
public/scripts/showdown-underscore.js+1 -1
@@ -9,7 +9,7 @@ export const markdownUnderscoreExt = () => {
9 return [{9 return [{
10 type: 'output',10 type: 'output',
11 regex: new RegExp('(<code(?:\\s+[^>]*)?>[\\s\\S]*?<\\/code>|<style(?:\\s+[^>]*)?>[\\s\\S]*?<\\/style>)|\\b(?<!_)_(?!_)(.*?)(?<!_)_(?!_)\\b', 'gi'),11 regex: new RegExp('(<code(?:\\s+[^>]*)?>[\\s\\S]*?<\\/code>|<style(?:\\s+[^>]*)?>[\\s\\S]*?<\\/style>)|\\b(?<!_)_(?!_)(.*?)(?<!_)_(?!_)\\b', 'gi'),
12 replace: function(match, tagContent, italicContent) {12 replace: function (match, tagContent, italicContent) {
13 if (tagContent) {13 if (tagContent) {
14 // If it's inside <code> or <style> tags, return unchanged14 // If it's inside <code> or <style> tags, return unchanged
15 return match;15 return match;
public/scripts/slash-commands.js+0 -2
@@ -4916,7 +4916,6 @@ export async function sendNarratorMessage(args, text) {
4916}4916}
49174917
4918export async function promptQuietForLoudResponse(who, text) {4918export async function promptQuietForLoudResponse(who, text) {
4919
4920 let character_id = getContext().characterId;4919 let character_id = getContext().characterId;
4921 if (who === 'sys') {4920 if (who === 'sys') {
4922 text = 'System: ' + text;4921 text = 'System: ' + text;
@@ -4955,7 +4954,6 @@ export async function promptQuietForLoudResponse(who, text) {
4955 addOneMessage(message);4954 addOneMessage(message);
4956 await eventSource.emit(event_types.USER_MESSAGE_RENDERED, (chat.length - 1));4955 await eventSource.emit(event_types.USER_MESSAGE_RENDERED, (chat.length - 1));
4957 await saveChatConditional();4956 await saveChatConditional();
4958
4959}4957}
49604958
4961async function sendCommentMessage(args, text) {4959async function sendCommentMessage(args, text) {
public/scripts/slash-commands/SlashCommand.js+3 -6
@@ -26,7 +26,6 @@ import { SlashCommandScope } from './SlashCommandScope.js';
26*/26*/
2727
2828
29
30export class SlashCommand {29export class SlashCommand {
31 /**30 /**
32 * Creates a SlashCommand from a properties object.31 * Creates a SlashCommand from a properties object.
@@ -48,8 +47,6 @@ export class SlashCommand {
48 }47 }
4948
5049
51
52
53 /**@type {string}*/ name;50 /**@type {string}*/ name;
54 /**@type {(namedArguments:NamedArguments, unnamedArguments:UnnamedArguments)=>string|SlashCommandClosure|Promise<string|SlashCommandClosure>}*/ callback;51 /**@type {(namedArguments:NamedArguments, unnamedArguments:UnnamedArguments)=>string|SlashCommandClosure|Promise<string|SlashCommandClosure>}*/ callback;
55 /**@type {string}*/ helpString;52 /**@type {string}*/ helpString;
@@ -86,7 +83,7 @@ export class SlashCommand {
86 name.classList.add('name');83 name.classList.add('name');
87 name.classList.add('monospace');84 name.classList.add('monospace');
88 name.textContent = '/';85 name.textContent = '/';
89 key.split('').forEach(char=>{86 key.split('').forEach(char => {
90 const span = document.createElement('span'); {87 const span = document.createElement('span'); {
91 span.textContent = char;88 span.textContent = char;
92 name.append(span);89 name.append(span);
@@ -229,7 +226,7 @@ export class SlashCommand {
229 const unnamedArguments = cmd.unnamedArgumentList ?? [];226 const unnamedArguments = cmd.unnamedArgumentList ?? [];
230 const returnType = cmd.returns ?? 'void';227 const returnType = cmd.returns ?? 'void';
231 const helpString = cmd.helpString ?? 'NO DETAILS';228 const helpString = cmd.helpString ?? 'NO DETAILS';
232 const aliasList = [cmd.name, ...(cmd.aliases ?? [])].filter(it=>it != key);229 const aliasList = [cmd.name, ...(cmd.aliases ?? [])].filter(it => it != key);
233 const specs = document.createElement('div'); {230 const specs = document.createElement('div'); {
234 specs.classList.add('specs');231 specs.classList.add('specs');
235 const head = document.createElement('div'); {232 const head = document.createElement('div'); {
@@ -257,7 +254,7 @@ export class SlashCommand {
257 this.isExtension ? 'Extension' : 'Core',254 this.isExtension ? 'Extension' : 'Core',
258 this.isThirdParty ? 'Third Party' : (this.isExtension ? 'Core' : null),255 this.isThirdParty ? 'Third Party' : (this.isExtension ? 'Core' : null),
259 this.source,256 this.source,
260 ].filter(it=>it).join('\n');257 ].filter(it => it).join('\n');
261 head.append(src);258 head.append(src);
262 }259 }
263 if (this.rawQuotes) {260 if (this.rawQuotes) {
public/scripts/slash-commands/SlashCommandArgument.js+1 -2
@@ -5,7 +5,6 @@ import { SlashCommandExecutor } from './SlashCommandExecutor.js';
5import { SlashCommandScope } from './SlashCommandScope.js';5import { SlashCommandScope } from './SlashCommandScope.js';
66
77
8
9/**@readonly*/8/**@readonly*/
10/**@enum {string}*/9/**@enum {string}*/
11export const ARGUMENT_TYPE = {10export const ARGUMENT_TYPE = {
@@ -68,7 +67,7 @@ export class SlashCommandArgument {
68 this.isRequired = isRequired ?? false;67 this.isRequired = isRequired ?? false;
69 this.acceptsMultiple = acceptsMultiple ?? false;68 this.acceptsMultiple = acceptsMultiple ?? false;
70 this.defaultValue = defaultValue;69 this.defaultValue = defaultValue;
71 this.enumList = (enums ? Array.isArray(enums) ? enums : [enums] : []).map(it=>{70 this.enumList = (enums ? Array.isArray(enums) ? enums : [enums] : []).map(it => {
72 if (it instanceof SlashCommandEnumValue) return it;71 if (it instanceof SlashCommandEnumValue) return it;
73 return new SlashCommandEnumValue(it);72 return new SlashCommandEnumValue(it);
74 });73 });
public/scripts/slash-commands/SlashCommandAutoCompleteNameResult.js+13 -13
@@ -22,11 +22,11 @@ export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult {
22 executor.start,22 executor.start,
23 Object23 Object
24 .keys(commands)24 .keys(commands)
25 .map(key=>new SlashCommandCommandAutoCompleteOption(commands[key], key))25 .map(key => new SlashCommandCommandAutoCompleteOption(commands[key], key))
26 ,26 ,
27 false,27 false,
28 ()=>`No matching slash commands for "/${this.name}"`,28 () => `No matching slash commands for "/${this.name}"`,
29 ()=>'No slash commands found!',29 () => 'No slash commands found!',
30 );30 );
31 this.executor = executor;31 this.executor = executor;
32 this.scope = scope;32 this.scope = scope;
@@ -63,7 +63,7 @@ export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult {
63 if (!Array.isArray(this.executor.command?.namedArgumentList)) {63 if (!Array.isArray(this.executor.command?.namedArgumentList)) {
64 return null;64 return null;
65 }65 }
66 const notProvidedNamedArguments = this.executor.command.namedArgumentList.filter(arg=>!this.executor.namedArgumentList.find(it=>it.name == arg.name));66 const notProvidedNamedArguments = this.executor.command.namedArgumentList.filter(arg => !this.executor.namedArgumentList.find(it => it.name == arg.name));
67 let name;67 let name;
68 let value;68 let value;
69 let start;69 let start;
@@ -73,13 +73,13 @@ export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult {
73 const namedArgsFollowedBySpace = text[this.executor.endNamedArgs] == ' ';73 const namedArgsFollowedBySpace = text[this.executor.endNamedArgs] == ' ';
74 if (this.executor.startNamedArgs <= index && this.executor.endNamedArgs + (namedArgsFollowedBySpace ? 1 : 0) >= index) {74 if (this.executor.startNamedArgs <= index && this.executor.endNamedArgs + (namedArgsFollowedBySpace ? 1 : 0) >= index) {
75 // cursor is somewhere within the named arguments (including final space)75 // cursor is somewhere within the named arguments (including final space)
76 argAssign = this.executor.namedArgumentList.find(it=>it.start <= index && it.end >= index);76 argAssign = this.executor.namedArgumentList.find(it => it.start <= index && it.end >= index);
77 if (argAssign) {77 if (argAssign) {
78 const [argName, ...v] = text.slice(argAssign.start, index).split(getSplitRegex());78 const [argName, ...v] = text.slice(argAssign.start, index).split(getSplitRegex());
79 name = argName;79 name = argName;
80 value = v.join('');80 value = v.join('');
81 start = argAssign.start;81 start = argAssign.start;
82 cmdArg = this.executor.command.namedArgumentList.find(it=>[it.name, `${it.name}=`].includes(argAssign.name));82 cmdArg = this.executor.command.namedArgumentList.find(it => [it.name, `${it.name}=`].includes(argAssign.name));
83 if (cmdArg) notProvidedNamedArguments.push(cmdArg);83 if (cmdArg) notProvidedNamedArguments.push(cmdArg);
84 } else {84 } else {
85 name = '';85 name = '';
@@ -106,13 +106,13 @@ export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult {
106 // if cursor is already behind "=" check for enums106 // if cursor is already behind "=" check for enums
107 const enumList = cmdArg?.enumProvider?.(this.executor, this.scope) ?? cmdArg?.enumList;107 const enumList = cmdArg?.enumProvider?.(this.executor, this.scope) ?? cmdArg?.enumList;
108 if (cmdArg && enumList?.length) {108 if (cmdArg && enumList?.length) {
109 if (isSelect && enumList.find(it=>it.value == value) && argAssign && argAssign.end == index) {109 if (isSelect && enumList.find(it => it.value == value) && argAssign && argAssign.end == index) {
110 return null;110 return null;
111 }111 }
112 const result = new AutoCompleteSecondaryNameResult(112 const result = new AutoCompleteSecondaryNameResult(
113 value,113 value,
114 start + name.length,114 start + name.length,
115 enumList.map(it=>SlashCommandEnumAutoCompleteOption.from(this.executor.command, it)),115 enumList.map(it => SlashCommandEnumAutoCompleteOption.from(this.executor.command, it)),
116 true,116 true,
117 );117 );
118 result.isRequired = true;118 result.isRequired = true;
@@ -125,10 +125,10 @@ export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult {
125 const result = new AutoCompleteSecondaryNameResult(125 const result = new AutoCompleteSecondaryNameResult(
126 name,126 name,
127 start,127 start,
128 notProvidedNamedArguments.map(it=>new SlashCommandNamedArgumentAutoCompleteOption(it, this.executor.command)),128 notProvidedNamedArguments.map(it => new SlashCommandNamedArgumentAutoCompleteOption(it, this.executor.command)),
129 false,129 false,
130 );130 );
131 result.isRequired = notProvidedNamedArguments.find(it=>it.isRequired) != null;131 result.isRequired = notProvidedNamedArguments.find(it => it.isRequired) != null;
132 return result;132 return result;
133 }133 }
134134
@@ -147,7 +147,7 @@ export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult {
147 let argAssign;147 let argAssign;
148 if (this.executor.startUnnamedArgs <= index && this.executor.endUnnamedArgs + 1 >= index) {148 if (this.executor.startUnnamedArgs <= index && this.executor.endUnnamedArgs + 1 >= index) {
149 // cursor is somwehere in the unnamed args149 // cursor is somwehere in the unnamed args
150 const idx = this.executor.unnamedArgumentList.findIndex(it=>it.start <= index && it.end >= index);150 const idx = this.executor.unnamedArgumentList.findIndex(it => it.start <= index && it.end >= index);
151 if (idx > -1) {151 if (idx > -1) {
152 argAssign = this.executor.unnamedArgumentList[idx];152 argAssign = this.executor.unnamedArgumentList[idx];
153 cmdArg = this.executor.command.unnamedArgumentList[idx];153 cmdArg = this.executor.command.unnamedArgumentList[idx];
@@ -179,10 +179,10 @@ export class SlashCommandAutoCompleteNameResult extends AutoCompleteNameResult {
179 const result = new AutoCompleteSecondaryNameResult(179 const result = new AutoCompleteSecondaryNameResult(
180 value,180 value,
181 start,181 start,
182 enumList.map(it=>SlashCommandEnumAutoCompleteOption.from(this.executor.command, it)),182 enumList.map(it => SlashCommandEnumAutoCompleteOption.from(this.executor.command, it)),
183 false,183 false,
184 );184 );
185 const isCompleteValue = enumList.find(it=>it.value == value);185 const isCompleteValue = enumList.find(it => it.value == value);
186 const isSelectedValue = isSelect && isCompleteValue;186 const isSelectedValue = isSelect && isCompleteValue;
187 result.isRequired = cmdArg.isRequired && !isSelectedValue;187 result.isRequired = cmdArg.isRequired && !isSelectedValue;
188 result.forceMatch = cmdArg.forceEnum;188 result.forceMatch = cmdArg.forceEnum;
public/scripts/slash-commands/SlashCommandBrowser.js+15 -15
@@ -25,7 +25,7 @@ export class SlashCommandBrowser {
25 inp.classList.add('text_pole');25 inp.classList.add('text_pole');
26 inp.type = 'search';26 inp.type = 'search';
27 inp.placeholder = 'Search slash commands - use quotes to search "literal" instead of fuzzy';27 inp.placeholder = 'Search slash commands - use quotes to search "literal" instead of fuzzy';
28 inp.addEventListener('input', ()=>{28 inp.addEventListener('input', () => {
29 this.details?.remove();29 this.details?.remove();
30 this.details = null;30 this.details = null;
31 let query = inp.value.trim();31 let query = inp.value.trim();
@@ -38,7 +38,7 @@ export class SlashCommandBrowser {
38 const match = queryRegex.exec(query);38 const match = queryRegex.exec(query);
39 if (!match) break;39 if (!match) break;
40 if (match[1] !== undefined) {40 if (match[1] !== undefined) {
41 fuzzyList.push(new RegExp(`^(.*?)${match[1].split('').map(char=>`(${escapeRegex(char)})`).join('(.*?)')}(.*?)$`, 'i'));41 fuzzyList.push(new RegExp(`^(.*?)${match[1].split('').map(char => `(${escapeRegex(char)})`).join('(.*?)')}(.*?)$`, 'i'));
42 } else if (match[2] !== undefined) {42 } else if (match[2] !== undefined) {
43 quotedList.push(match[2]);43 quotedList.push(match[2]);
44 }44 }
@@ -47,17 +47,17 @@ export class SlashCommandBrowser {
47 for (const cmd of this.cmdList) {47 for (const cmd of this.cmdList) {
48 const targets = [48 const targets = [
49 cmd.name,49 cmd.name,
50 ...cmd.namedArgumentList.map(it=>it.name),50 ...cmd.namedArgumentList.map(it => it.name),
51 ...cmd.namedArgumentList.map(it=>it.description),51 ...cmd.namedArgumentList.map(it => it.description),
52 ...cmd.namedArgumentList.map(it=>it.enumList.map(e=>e.value)).flat(),52 ...cmd.namedArgumentList.map(it => it.enumList.map(e => e.value)).flat(),
53 ...cmd.namedArgumentList.map(it=>it.typeList).flat(),53 ...cmd.namedArgumentList.map(it => it.typeList).flat(),
54 ...cmd.unnamedArgumentList.map(it=>it.description),54 ...cmd.unnamedArgumentList.map(it => it.description),
55 ...cmd.unnamedArgumentList.map(it=>it.enumList.map(e=>e.value)).flat(),55 ...cmd.unnamedArgumentList.map(it => it.enumList.map(e => e.value)).flat(),
56 ...cmd.unnamedArgumentList.map(it=>it.typeList).flat(),56 ...cmd.unnamedArgumentList.map(it => it.typeList).flat(),
57 ...cmd.aliases,57 ...cmd.aliases,
58 cmd.helpString,58 cmd.helpString,
59 ];59 ];
60 const find = ()=>targets.find(t=>(fuzzyList.find(f=>f.test(t)) ?? quotedList.find(q=>t.includes(q))) !== undefined) !== undefined;60 const find = () => targets.find(t => (fuzzyList.find(f => f.test(t)) ?? quotedList.find(q => t.includes(q))) !== undefined) !== undefined;
61 if (fuzzyList.length + quotedList.length === 0 || find()) {61 if (fuzzyList.length + quotedList.length === 0 || find()) {
62 this.itemMap[cmd.name].classList.remove('isFiltered');62 this.itemMap[cmd.name].classList.remove('isFiltered');
63 } else {63 } else {
@@ -85,7 +85,7 @@ export class SlashCommandBrowser {
85 const item = cmd.renderHelpItem();85 const item = cmd.renderHelpItem();
86 this.itemMap[cmd.name] = item;86 this.itemMap[cmd.name] = item;
87 let details;87 let details;
88 item.addEventListener('click', ()=>{88 item.addEventListener('click', () => {
89 if (!details) {89 if (!details) {
90 details = document.createElement('div'); {90 details = document.createElement('div'); {
91 details.classList.add('autoComplete-detailsWrap');91 details.classList.add('autoComplete-detailsWrap');
@@ -97,7 +97,7 @@ export class SlashCommandBrowser {
97 }97 }
98 }98 }
99 if (this.details !== details) {99 if (this.details !== details) {
100 Array.from(list.querySelectorAll('.selected')).forEach(it=>it.classList.remove('selected'));100 Array.from(list.querySelectorAll('.selected')).forEach(it => it.classList.remove('selected'));
101 item.classList.add('selected');101 item.classList.add('selected');
102 this.details?.remove();102 this.details?.remove();
103 container.append(details);103 container.append(details);
@@ -122,13 +122,13 @@ export class SlashCommandBrowser {
122 }122 }
123 parent.append(this.dom);123 parent.append(this.dom);
124124
125 this.mo = new MutationObserver(muts=>{125 this.mo = new MutationObserver(muts => {
126 if (muts.find(mut=>Array.from(mut.removedNodes).find(it=>it === this.dom || it.contains(this.dom)))) {126 if (muts.find(mut => Array.from(mut.removedNodes).find(it => it === this.dom || it.contains(this.dom)))) {
127 this.mo.disconnect();127 this.mo.disconnect();
128 window.removeEventListener('keydown', boundHandler);128 window.removeEventListener('keydown', boundHandler);
129 }129 }
130 });130 });
131 this.mo.observe(document.querySelector('#chat'), { childList:true, subtree:true });131 this.mo.observe(document.querySelector('#chat'), { childList: true, subtree: true });
132 const boundHandler = this.handleKeyDown.bind(this);132 const boundHandler = this.handleKeyDown.bind(this);
133 window.addEventListener('keydown', boundHandler);133 window.addEventListener('keydown', boundHandler);
134 return this.dom;134 return this.dom;
public/scripts/slash-commands/SlashCommandClosure.js+17 -17
@@ -37,7 +37,7 @@ export class SlashCommandClosure {
3737
38 /**@type {number}*/38 /**@type {number}*/
39 get commandCount() {39 get commandCount() {
40 return this.executorList.map(executor=>executor.commandCount).reduce((sum,cur)=>sum + cur, 0);40 return this.executorList.map(executor => executor.commandCount).reduce((sum, cur) => sum + cur, 0);
41 }41 }
4242
43 constructor(parent) {43 constructor(parent) {
@@ -156,7 +156,7 @@ export class SlashCommandClosure {
156 let isList = false;156 let isList = false;
157 let listValues = [];157 let listValues = [];
158 scope = scope ?? this.scope;158 scope = scope ?? this.scope;
159 const escapeMacro = (it, isAnchored = false)=>{159 const escapeMacro = (it, isAnchored = false) => {
160 const regexText = escapeRegex(it.key.replace(/\*/g, '~~~WILDCARD~~~'))160 const regexText = escapeRegex(it.key.replace(/\*/g, '~~~WILDCARD~~~'))
161 .replaceAll('~~~WILDCARD~~~', '(?:(?:(?!(?:::|}})).)*)')161 .replaceAll('~~~WILDCARD~~~', '(?:(?:(?!(?:::|}})).)*)')
162 ;162 ;
@@ -165,7 +165,7 @@ export class SlashCommandClosure {
165 }165 }
166 return regexText;166 return regexText;
167 };167 };
168 const macroList = scope.macroList.toSorted((a,b)=>{168 const macroList = scope.macroList.toSorted((a, b) => {
169 if (a.key.includes('*') && !b.key.includes('*')) return 1;169 if (a.key.includes('*') && !b.key.includes('*')) return 1;
170 if (!a.key.includes('*') && b.key.includes('*')) return -1;170 if (!a.key.includes('*') && b.key.includes('*')) return -1;
171 if (a.key.includes('*') && b.key.includes('*')) return b.key.indexOf('*') - a.key.indexOf('*');171 if (a.key.includes('*') && b.key.includes('*')) return b.key.indexOf('*') - a.key.indexOf('*');
@@ -174,7 +174,7 @@ export class SlashCommandClosure {
174 if (power_user.experimental_macro_engine) {174 if (power_user.experimental_macro_engine) {
175 return this.substituteWithMacroEngine(text, scope, macroList);175 return this.substituteWithMacroEngine(text, scope, macroList);
176 }176 }
177 const macros = macroList.map(it=>escapeMacro(it)).join('|');177 const macros = macroList.map(it => escapeMacro(it)).join('|');
178 const re = new RegExp(`(?<pipe>{{pipe}})|(?:{{var::(?<var>[^\\s]+?)(?:::(?<varIndex>(?!}}).+))?}})|(?:{{(?<macro>${macros})}})`);178 const re = new RegExp(`(?<pipe>{{pipe}})|(?:{{var::(?<var>[^\\s]+?)(?:::(?<varIndex>(?!}}).+))?}})|(?:{{(?<macro>${macros})}})`);
179 let done = '';179 let done = '';
180 let remaining = text;180 let remaining = text;
@@ -182,7 +182,7 @@ export class SlashCommandClosure {
182 const match = re.exec(remaining);182 const match = re.exec(remaining);
183 const before = substituteParams(remaining.slice(0, match.index));183 const before = substituteParams(remaining.slice(0, match.index));
184 const after = remaining.slice(match.index + match[0].length);184 const after = remaining.slice(match.index + match[0].length);
185 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;185 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;
186 if (replacer instanceof SlashCommandClosure) {186 if (replacer instanceof SlashCommandClosure) {
187 replacer.abortController = this.abortController;187 replacer.abortController = this.abortController;
188 replacer.breakController = this.breakController;188 replacer.breakController = this.breakController;
@@ -253,7 +253,7 @@ export class SlashCommandClosure {
253 return step.value;253 return step.value;
254 }254 }
255255
256 async * executeDirect() {256 async* executeDirect() {
257 this.debugController?.down(this);257 this.debugController?.down(this);
258 // closure arguments258 // closure arguments
259 for (const arg of this.argumentList) {259 for (const arg of this.argumentList) {
@@ -325,10 +325,10 @@ export class SlashCommandClosure {
325 // breakpoint has to yield before arguments are resolved if one of the325 // breakpoint has to yield before arguments are resolved if one of the
326 // arguments is an immediate closure, otherwise you cannot step into the326 // arguments is an immediate closure, otherwise you cannot step into the
327 // immediate closure327 // immediate closure
328 const hasImmediateClosureInNamedArgs = /**@type {SlashCommandExecutor}*/(step.value)?.namedArgumentList?.find(it=>it.value instanceof SlashCommandClosure && it.value.executeNow);328 const hasImmediateClosureInNamedArgs = /**@type {SlashCommandExecutor}*/(step.value)?.namedArgumentList?.find(it => it.value instanceof SlashCommandClosure && it.value.executeNow);
329 const hasImmediateClosureInUnnamedArgs = /**@type {SlashCommandExecutor}*/(step.value)?.unnamedArgumentList?.find(it=>it.value instanceof SlashCommandClosure && it.value.executeNow);329 const hasImmediateClosureInUnnamedArgs = /**@type {SlashCommandExecutor}*/(step.value)?.unnamedArgumentList?.find(it => it.value instanceof SlashCommandClosure && it.value.executeNow);
330 if (hasImmediateClosureInNamedArgs || hasImmediateClosureInUnnamedArgs) {330 if (hasImmediateClosureInNamedArgs || hasImmediateClosureInUnnamedArgs) {
331 this.debugController.isStepping = yield { closure:this, executor:step.value };331 this.debugController.isStepping = yield { closure: this, executor: step.value };
332 } else {332 } else {
333 this.debugController.isStepping = true;333 this.debugController.isStepping = true;
334 this.debugController.stepStack[this.debugController.stepStack.length - 1] = true;334 this.debugController.stepStack[this.debugController.stepStack.length - 1] = true;
@@ -338,10 +338,10 @@ export class SlashCommandClosure {
338 this.debugController.isSteppingInto = false;338 this.debugController.isSteppingInto = false;
339 // if stepping, have to yield before arguments are resolved if one of the arguments339 // if stepping, have to yield before arguments are resolved if one of the arguments
340 // is an immediate closure, otherwise you cannot step into the immediate closure340 // is an immediate closure, otherwise you cannot step into the immediate closure
341 const hasImmediateClosureInNamedArgs = /**@type {SlashCommandExecutor}*/(step.value)?.namedArgumentList?.find(it=>it.value instanceof SlashCommandClosure && it.value.executeNow);341 const hasImmediateClosureInNamedArgs = /**@type {SlashCommandExecutor}*/(step.value)?.namedArgumentList?.find(it => it.value instanceof SlashCommandClosure && it.value.executeNow);
342 const hasImmediateClosureInUnnamedArgs = /**@type {SlashCommandExecutor}*/(step.value)?.unnamedArgumentList?.find(it=>it.value instanceof SlashCommandClosure && it.value.executeNow);342 const hasImmediateClosureInUnnamedArgs = /**@type {SlashCommandExecutor}*/(step.value)?.unnamedArgumentList?.find(it => it.value instanceof SlashCommandClosure && it.value.executeNow);
343 if (hasImmediateClosureInNamedArgs || hasImmediateClosureInUnnamedArgs) {343 if (hasImmediateClosureInNamedArgs || hasImmediateClosureInUnnamedArgs) {
344 this.debugController.isStepping = yield { closure:this, executor:step.value };344 this.debugController.isStepping = yield { closure: this, executor: step.value };
345 }345 }
346 }346 }
347 // resolve args347 // resolve args
@@ -354,7 +354,7 @@ export class SlashCommandClosure {
354 }354 }
355 } else if (!step.done && this.debugController?.testStepping(this)) {355 } else if (!step.done && this.debugController?.testStepping(this)) {
356 this.debugController.isSteppingInto = false;356 this.debugController.isSteppingInto = false;
357 this.debugController.isStepping = yield { closure:this, executor:step.value };357 this.debugController.isStepping = yield { closure: this, executor: step.value };
358 }358 }
359 // execute executor359 // execute executor
360 step = await stepper.next();360 step = await stepper.next();
@@ -377,7 +377,7 @@ export class SlashCommandClosure {
377 * - after arguments are resolved377 * - after arguments are resolved
378 * - after execution378 * - after execution
379 */379 */
380 async * executeStep() {380 async* executeStep() {
381 let done = 0;381 let done = 0;
382 let isFirst = true;382 let isFirst = true;
383 for (const executor of this.executorList) {383 for (const executor of this.executorList) {
@@ -429,7 +429,7 @@ export class SlashCommandClosure {
429 // then yield for "before exec"429 // then yield for "before exec"
430 yield executor;430 yield executor;
431 // followed by command execution431 // followed by command execution
432 executor.onProgress = (subDone, subTotal)=>this.onProgress?.(done + subDone, this.commandCount);432 executor.onProgress = (subDone, subTotal) => this.onProgress?.(done + subDone, this.commandCount);
433 const isStepping = this.debugController?.testStepping(this);433 const isStepping = this.debugController?.testStepping(this);
434 if (this.debugController) {434 if (this.debugController) {
435 this.debugController.isStepping = false || this.debugController.isSteppingInto;435 this.debugController.isStepping = false || this.debugController.isSteppingInto;
@@ -586,7 +586,7 @@ export class SlashCommandClosure {
586 if (!executor.command.splitUnnamedArgument) {586 if (!executor.command.splitUnnamedArgument) {
587 if (value.length == 1) {587 if (value.length == 1) {
588 value = value[0];588 value = value[0];
589 } else if (!value.find(it=>it instanceof SlashCommandClosure)) {589 } else if (!value.find(it => it instanceof SlashCommandClosure)) {
590 value = value.join('');590 value = value.join('');
591 }591 }
592 }592 }
@@ -598,7 +598,7 @@ export class SlashCommandClosure {
598 ?.replace(/\\\}/g, '}')598 ?.replace(/\\\}/g, '}')
599 ;599 ;
600 } else if (Array.isArray(value)) {600 } else if (Array.isArray(value)) {
601 value = value.map(v=>{601 value = value.map(v => {
602 if (typeof v == 'string') {602 if (typeof v == 'string') {
603 return v603 return v
604 ?.replace(/\\\{/g, '{')604 ?.replace(/\\\{/g, '{')
public/scripts/slash-commands/SlashCommandCommandAutoCompleteOption.js+0 -2
@@ -10,8 +10,6 @@ export class SlashCommandCommandAutoCompleteOption extends AutoCompleteOption {
10 }10 }
1111
1212
13
14
15 /**13 /**
16 * @param {SlashCommand} command14 * @param {SlashCommand} command
17 * @param {string} name15 * @param {string} name
public/scripts/slash-commands/SlashCommandCommonEnumsProvider.js+1 -1
@@ -155,7 +155,7 @@ export const commonEnumProviders = {
155 ...isAll || types.includes('scope') ? scope.allVariableNames.map(name => new SlashCommandEnumValue(name, null, enumTypes.variable, enumIcons.scopeVariable)) : [],155 ...isAll || types.includes('scope') ? scope.allVariableNames.map(name => new SlashCommandEnumValue(name, null, enumTypes.variable, enumIcons.scopeVariable)) : [],
156 ...isAll || types.includes('local') ? Object.keys(chat_metadata.variables ?? []).map(name => new SlashCommandEnumValue(name, null, enumTypes.name, enumIcons.localVariable)) : [],156 ...isAll || types.includes('local') ? Object.keys(chat_metadata.variables ?? []).map(name => new SlashCommandEnumValue(name, null, enumTypes.name, enumIcons.localVariable)) : [],
157 ...isAll || types.includes('global') ? Object.keys(extension_settings.variables.global ?? []).map(name => new SlashCommandEnumValue(name, null, enumTypes.macro, enumIcons.globalVariable)) : [],157 ...isAll || types.includes('global') ? Object.keys(extension_settings.variables.global ?? []).map(name => new SlashCommandEnumValue(name, null, enumTypes.macro, enumIcons.globalVariable)) : [],
158 ].filter((item, idx, list)=>idx == list.findIndex(it=>it.value == item.value));158 ].filter((item, idx, list) => idx == list.findIndex(it => it.value == item.value));
159 },159 },
160160
161 /**161 /**
public/scripts/slash-commands/SlashCommandDebugController.js+4 -9
@@ -18,15 +18,11 @@ export class SlashCommandDebugController {
18 /** @type {(closure:SlashCommandClosure, executor:SlashCommandExecutor)=>Promise<boolean>} */ onBreakPoint;18 /** @type {(closure:SlashCommandClosure, executor:SlashCommandExecutor)=>Promise<boolean>} */ onBreakPoint;
1919
2020
21
22
23 testStepping(closure) {21 testStepping(closure) {
24 return this.stepStack[this.stack.indexOf(closure)];22 return this.stepStack[this.stack.indexOf(closure)];
25 }23 }
2624
2725
28
29
30 down(closure) {26 down(closure) {
31 this.stack.push(closure);27 this.stack.push(closure);
32 if (this.stepStack.length < this.stack.length) {28 if (this.stepStack.length < this.stack.length) {
@@ -44,20 +40,19 @@ export class SlashCommandDebugController {
44 }40 }
4541
4642
47
48 resume() {43 resume() {
49 this.continueResolver?.(false);44 this.continueResolver?.(false);
50 this.continuePromise = null;45 this.continuePromise = null;
51 this.stepStack.forEach((_,idx)=>this.stepStack[idx] = false);46 this.stepStack.forEach((_, idx) => this.stepStack[idx] = false);
52 }47 }
53 step() {48 step() {
54 this.stepStack.forEach((_,idx)=>this.stepStack[idx] = true);49 this.stepStack.forEach((_, idx) => this.stepStack[idx] = true);
55 this.continueResolver?.(true);50 this.continueResolver?.(true);
56 this.continuePromise = null;51 this.continuePromise = null;
57 }52 }
58 stepInto() {53 stepInto() {
59 this.isSteppingInto = true;54 this.isSteppingInto = true;
60 this.stepStack.forEach((_,idx)=>this.stepStack[idx] = true);55 this.stepStack.forEach((_, idx) => this.stepStack[idx] = true);
61 this.continueResolver?.(true);56 this.continueResolver?.(true);
62 this.continuePromise = null;57 this.continuePromise = null;
63 }58 }
@@ -69,7 +64,7 @@ export class SlashCommandDebugController {
69 }64 }
7065
71 async awaitContinue() {66 async awaitContinue() {
72 this.continuePromise ??= new Promise(resolve=>{67 this.continuePromise ??= new Promise(resolve => {
73 this.continueResolver = resolve;68 this.continueResolver = resolve;
74 });69 });
75 this.isStepping = await this.continuePromise;70 this.isStepping = await this.continuePromise;
public/scripts/slash-commands/SlashCommandEnumAutoCompleteOption.js+1 -2
@@ -9,7 +9,7 @@ export class SlashCommandEnumAutoCompleteOption extends AutoCompleteOption {
9 * @returns {SlashCommandEnumAutoCompleteOption}9 * @returns {SlashCommandEnumAutoCompleteOption}
10 */10 */
11 static from(cmd, enumValue) {11 static from(cmd, enumValue) {
12 const mapped = this.valueToOptionMap.find(it=>enumValue instanceof it.value)?.option ?? this;12 const mapped = this.valueToOptionMap.find(it => enumValue instanceof it.value)?.option ?? this;
13 return new mapped(cmd, enumValue);13 return new mapped(cmd, enumValue);
14 }14 }
15 /**@type {{value:(typeof SlashCommandEnumValue), option:(typeof SlashCommandEnumAutoCompleteOption)}[]} */15 /**@type {{value:(typeof SlashCommandEnumValue), option:(typeof SlashCommandEnumAutoCompleteOption)}[]} */
@@ -18,7 +18,6 @@ export class SlashCommandEnumAutoCompleteOption extends AutoCompleteOption {
18 /**@type {SlashCommandEnumValue}*/ enumValue;18 /**@type {SlashCommandEnumValue}*/ enumValue;
1919
2020
21
22 /**21 /**
23 * @param {SlashCommand} cmd22 * @param {SlashCommand} cmd
24 * @param {SlashCommandEnumValue} enumValue23 * @param {SlashCommandEnumValue} enumValue
public/scripts/slash-commands/SlashCommandExecutionError.js+0 -1
@@ -48,7 +48,6 @@ export class SlashCommandExecutionError extends Error {
48 }48 }
4949
5050
51
52 constructor(cause, message, commandName, start, end, commandText, fullText) {51 constructor(cause, message, commandName, start, end, commandText, fullText) {
53 super(message, { cause });52 super(message, { cause });
54 this.commandName = commandName;53 this.commandName = commandName;
public/scripts/slash-commands/SlashCommandExecutor.js+6 -6
@@ -17,10 +17,10 @@ export class SlashCommandExecutor {
17 get source() { return this.#source; }17 get source() { return this.#source; }
18 set source(value) {18 set source(value) {
19 this.#source = value;19 this.#source = value;
20 for (const arg of this.namedArgumentList.filter(it=>it.value instanceof SlashCommandClosure)) {20 for (const arg of this.namedArgumentList.filter(it => it.value instanceof SlashCommandClosure)) {
21 arg.value.source = value;21 arg.value.source = value;
22 }22 }
23 for (const arg of this.unnamedArgumentList.filter(it=>it.value instanceof SlashCommandClosure)) {23 for (const arg of this.unnamedArgumentList.filter(it => it.value instanceof SlashCommandClosure)) {
24 arg.value.source = value;24 arg.value.source = value;
25 }25 }
26 }26 }
@@ -31,15 +31,15 @@ export class SlashCommandExecutor {
3131
32 get commandCount() {32 get commandCount() {
33 return 133 return 1
34 + this.namedArgumentList.filter(it=>it.value instanceof SlashCommandClosure).map(it=>/**@type {SlashCommandClosure}*/(it.value).commandCount).reduce((cur, sum)=>cur + sum, 0)34 + this.namedArgumentList.filter(it => it.value instanceof SlashCommandClosure).map(it =>/**@type {SlashCommandClosure}*/(it.value).commandCount).reduce((cur, sum) => cur + sum, 0)
35 + this.unnamedArgumentList.filter(it=>it.value instanceof SlashCommandClosure).map(it=>/**@type {SlashCommandClosure}*/(it.value).commandCount).reduce((cur, sum)=>cur + sum, 0)35 + this.unnamedArgumentList.filter(it => it.value instanceof SlashCommandClosure).map(it =>/**@type {SlashCommandClosure}*/(it.value).commandCount).reduce((cur, sum) => cur + sum, 0)
36 ;36 ;
37 }37 }
3838
39 set onProgress(value) {39 set onProgress(value) {
40 const closures = /**@type {SlashCommandClosure[]}*/([40 const closures = /**@type {SlashCommandClosure[]}*/([
41 ...this.namedArgumentList.filter(it=>it.value instanceof SlashCommandClosure).map(it=>it.value),41 ...this.namedArgumentList.filter(it => it.value instanceof SlashCommandClosure).map(it => it.value),
42 ...this.unnamedArgumentList.filter(it=>it.value instanceof SlashCommandClosure).map(it=>it.value),42 ...this.unnamedArgumentList.filter(it => it.value instanceof SlashCommandClosure).map(it => it.value),
43 ]);43 ]);
44 for (const closure of closures) {44 for (const closure of closures) {
45 closure.onProgress = value;45 closure.onProgress = value;
public/scripts/slash-commands/SlashCommandParser.js+20 -20
@@ -65,7 +65,7 @@ export class SlashCommandParser {
65 static addCommandObject(command) {65 static addCommandObject(command) {
66 const reserved = ['/', '#', ':', 'parser-flag', 'breakpoint'];66 const reserved = ['/', '#', ':', 'parser-flag', 'breakpoint'];
67 for (const start of reserved) {67 for (const start of reserved) {
68 if (command.name.toLowerCase().startsWith(start) || (command.aliases ?? []).find(a=>a.toLowerCase().startsWith(start))) {68 if (command.name.toLowerCase().startsWith(start) || (command.aliases ?? []).find(a => a.toLowerCase().startsWith(start))) {
69 throw new Error(`Illegal Name. Slash command name cannot begin with "${start}".`);69 throw new Error(`Illegal Name. Slash command name cannot begin with "${start}".`);
70 }70 }
71 }71 }
@@ -80,15 +80,15 @@ export class SlashCommandParser {
80 console.trace('WARN: Duplicate slash command registered!', [command.name, ...command.aliases]);80 console.trace('WARN: Duplicate slash command registered!', [command.name, ...command.aliases]);
81 }81 }
8282
83 const stack = new Error().stack.split('\n').map(it=>it.trim());83 const stack = new Error().stack.split('\n').map(it => it.trim());
84 command.isExtension = stack.find(it=>it.includes('/scripts/extensions/')) != null;84 command.isExtension = stack.find(it => it.includes('/scripts/extensions/')) != null;
85 command.isThirdParty = stack.find(it=>it.includes('/scripts/extensions/third-party/')) != null;85 command.isThirdParty = stack.find(it => it.includes('/scripts/extensions/third-party/')) != null;
86 if (command.isThirdParty) {86 if (command.isThirdParty) {
87 command.source = stack.find(it=>it.includes('/scripts/extensions/third-party/')).replace(/^.*?\/scripts\/extensions\/third-party\/([^/]+)\/.*$/, '$1');87 command.source = stack.find(it => it.includes('/scripts/extensions/third-party/')).replace(/^.*?\/scripts\/extensions\/third-party\/([^/]+)\/.*$/, '$1');
88 } else if (command.isExtension) {88 } else if (command.isExtension) {
89 command.source = stack.find(it=>it.includes('/scripts/extensions/')).replace(/^.*?\/scripts\/extensions\/([^/]+)\/.*$/, '$1');89 command.source = stack.find(it => it.includes('/scripts/extensions/')).replace(/^.*?\/scripts\/extensions\/([^/]+)\/.*$/, '$1');
90 } else {90 } else {
91 const idx = stack.findLastIndex(it=>it.includes('at SlashCommandParser.')) + 1;91 const idx = stack.findLastIndex(it => it.includes('at SlashCommandParser.')) + 1;
92 command.source = stack[idx].replace(/^.*?\/((?:scripts\/)?(?:[^/]+)\.js).*$/, '$1');92 command.source = stack[idx].replace(/^.*?\/((?:scripts\/)?(?:[^/]+)\.js).*$/, '$1');
93 }93 }
9494
@@ -153,7 +153,7 @@ export class SlashCommandParser {
153 description: 'The parser flag to modify.',153 description: 'The parser flag to modify.',
154 typeList: [ARGUMENT_TYPE.STRING],154 typeList: [ARGUMENT_TYPE.STRING],
155 isRequired: true,155 isRequired: true,
156 enumList: Object.keys(PARSER_FLAG).map(flag=>new SlashCommandEnumValue(flag, help[PARSER_FLAG[flag]])),156 enumList: Object.keys(PARSER_FLAG).map(flag => new SlashCommandEnumValue(flag, help[PARSER_FLAG[flag]])),
157 }),157 }),
158 SlashCommandArgument.fromProps({158 SlashCommandArgument.fromProps({
159 description: 'The state of the parser flag to set.',159 description: 'The state of the parser flag to set.',
@@ -439,7 +439,7 @@ export class SlashCommandParser {
439 PIPEBREAK,439 PIPEBREAK,
440 PIPE,440 PIPE,
441 );441 );
442 hljs.registerLanguage('stscript', ()=>({442 hljs.registerLanguage('stscript', () => ({
443 case_insensitive: false,443 case_insensitive: false,
444 keywords: [],444 keywords: [],
445 contains: [445 contains: [
@@ -480,19 +480,19 @@ export class SlashCommandParser {
480 }480 }
481 }481 }
482 const executor = this.commandIndex482 const executor = this.commandIndex
483 .filter(it=>it.start <= index && (it.end >= index || it.end == null))483 .filter(it => it.start <= index && (it.end >= index || it.end == null))
484 .slice(-1)[0]484 .slice(-1)[0]
485 ?? null485 ?? null
486 ;486 ;
487487
488 if (executor) {488 if (executor) {
489 const childClosure = this.closureIndex489 const childClosure = this.closureIndex
490 .find(it=>it.start <= index && (it.end >= index || it.end == null) && it.start > executor.start)490 .find(it => it.start <= index && (it.end >= index || it.end == null) && it.start > executor.start)
491 ?? null491 ?? null
492 ;492 ;
493 if (childClosure !== null) return null;493 if (childClosure !== null) return null;
494 // Check if cursor is inside a macro494 // Check if cursor is inside a macro
495 const macroEntry = this.macroIndex.findLast(it=>it.start <= index && it.end >= index);495 const macroEntry = this.macroIndex.findLast(it => it.start <= index && it.end >= index);
496 if (macroEntry) {496 if (macroEntry) {
497 // Build macro info object for shared function497 // Build macro info object for shared function
498 const macroContent = text.slice(macroEntry.start + 2, macroEntry.end - (text.slice(macroEntry.end - 2, macroEntry.end) === '}}' ? 2 : 0));498 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 {
522 if (executor.name == ':') {522 if (executor.name == ':') {
523 const options = this.scopeIndex[this.commandIndex.indexOf(executor)]523 const options = this.scopeIndex[this.commandIndex.indexOf(executor)]
524 ?.allVariableNames524 ?.allVariableNames
525 ?.map(it=>new SlashCommandVariableAutoCompleteOption(it))525 ?.map(it => new SlashCommandVariableAutoCompleteOption(it))
526 ?? []526 ?? []
527 ;527 ;
528 try {528 try {
529 if ('quickReplyApi' in globalThis) {529 if ('quickReplyApi' in globalThis) {
530 const qrApi = globalThis.quickReplyApi;530 const qrApi = globalThis.quickReplyApi;
531 options.push(...qrApi.listSets()531 options.push(...qrApi.listSets()
532 .map(set=>qrApi.listQuickReplies(set).map(qr=>`${set}.${qr}`))532 .map(set => qrApi.listQuickReplies(set).map(qr => `${set}.${qr}`))
533 .flat()533 .flat()
534 .map(qr=>new SlashCommandQuickReplyAutoCompleteOption(qr)),534 .map(qr => new SlashCommandQuickReplyAutoCompleteOption(qr)),
535 );535 );
536 }536 }
537 } catch { /* empty */ }537 } catch { /* empty */ }
@@ -540,8 +540,8 @@ export class SlashCommandParser {
540 executor.start,540 executor.start,
541 options,541 options,
542 true,542 true,
543 ()=>`No matching variables in scope and no matching Quick Replies for "${result.name}"`,543 () => `No matching variables in scope and no matching Quick Replies for "${result.name}"`,
544 ()=>'No variables in scope and no Quick Replies found.',544 () => 'No variables in scope and no Quick Replies found.',
545 );545 );
546 return result;546 return result;
547 }547 }
@@ -741,7 +741,7 @@ export class SlashCommandParser {
741 return this.testSymbol(':}');741 return this.testSymbol(':}');
742 }742 }
743 parseClosure(isRoot = false) {743 parseClosure(isRoot = false) {
744 const closureIndexEntry = { start:this.index + 1, end:null };744 const closureIndexEntry = { start: this.index + 1, end: null };
745 this.closureIndex.push(closureIndexEntry);745 this.closureIndex.push(closureIndexEntry);
746 let injectPipe = true;746 let injectPipe = true;
747 if (!isRoot) this.take(2); // discard opening {:747 if (!isRoot) this.take(2); // discard opening {:
@@ -1023,14 +1023,14 @@ export class SlashCommandParser {
1023 cmd.unnamedArgumentList = this.parseUnnamedArgument(cmd.command?.unnamedArgumentList?.length && cmd?.command?.splitUnnamedArgument, cmd?.command?.splitUnnamedArgumentCount, rawQuotes);1023 cmd.unnamedArgumentList = this.parseUnnamedArgument(cmd.command?.unnamedArgumentList?.length && cmd?.command?.splitUnnamedArgument, cmd?.command?.splitUnnamedArgumentCount, rawQuotes);
1024 cmd.endUnnamedArgs = this.index;1024 cmd.endUnnamedArgs = this.index;
1025 if (cmd.name == 'let') {1025 if (cmd.name == 'let') {
1026 const keyArg = cmd.namedArgumentList.find(it=>it.name == 'key');1026 const keyArg = cmd.namedArgumentList.find(it => it.name == 'key');
1027 if (keyArg) {1027 if (keyArg) {
1028 this.scope.variableNames.push(keyArg.value.toString());1028 this.scope.variableNames.push(keyArg.value.toString());
1029 } else if (typeof cmd.unnamedArgumentList[0]?.value == 'string') {1029 } else if (typeof cmd.unnamedArgumentList[0]?.value == 'string') {
1030 this.scope.variableNames.push(cmd.unnamedArgumentList[0].value);1030 this.scope.variableNames.push(cmd.unnamedArgumentList[0].value);
1031 }1031 }
1032 } else if (cmd.name == 'import') {1032 } else if (cmd.name == 'import') {
1033 const value = /**@type {string[]}*/(cmd.unnamedArgumentList.map(it=>it.value));1033 const value = /**@type {string[]}*/(cmd.unnamedArgumentList.map(it => it.value));
1034 for (let i = 0; i < value.length; i++) {1034 for (let i = 0; i < value.length; i++) {
1035 const srcName = value[i];1035 const srcName = value[i];
1036 let dstName = srcName;1036 let dstName = srcName;
public/scripts/slash-commands/SlashCommandScope.js+3 -5
@@ -5,7 +5,7 @@ export class SlashCommandScope {
5 /** @type {string[]} */ variableNames = [];5 /** @type {string[]} */ variableNames = [];
6 get allVariableNames() {6 get allVariableNames() {
7 const names = [...this.variableNames, ...(this.parent?.allVariableNames ?? [])];7 const names = [...this.variableNames, ...(this.parent?.allVariableNames ?? [])];
8 return names.filter((it,idx)=>idx == names.indexOf(it));8 return names.filter((it, idx) => idx == names.indexOf(it));
9 }9 }
10 // @ts-ignore10 // @ts-ignore
11 /** @type {object.<string, string|SlashCommandClosure>} */ variables = {};11 /** @type {object.<string, string|SlashCommandClosure>} */ variables = {};
@@ -13,7 +13,7 @@ export class SlashCommandScope {
13 /** @type {object.<string, string|SlashCommandClosure>} */ macros = {};13 /** @type {object.<string, string|SlashCommandClosure>} */ macros = {};
14 /** @type {{key:string, value:string|SlashCommandClosure}[]} */14 /** @type {{key:string, value:string|SlashCommandClosure}[]} */
15 get macroList() {15 get macroList() {
16 return [...Object.keys(this.macros).map(key=>({ key, value:this.macros[key] })), ...(this.parent?.macroList ?? [])];16 return [...Object.keys(this.macros).map(key => ({ key, value: this.macros[key] })), ...(this.parent?.macroList ?? [])];
17 }17 }
18 /** @type {SlashCommandScope} */ parent;18 /** @type {SlashCommandScope} */ parent;
19 /** @type {string} */ #pipe;19 /** @type {string} */ #pipe;
@@ -40,7 +40,7 @@ export class SlashCommandScope {
4040
4141
42 setMacro(key, value, overwrite = true) {42 setMacro(key, value, overwrite = true) {
43 if (overwrite || !this.macroList.find(it=>it.key == key)) {43 if (overwrite || !this.macroList.find(it => it.key == key)) {
44 this.macros[key] = value;44 this.macros[key] = value;
45 }45 }
46 }46 }
@@ -109,8 +109,6 @@ export class SlashCommandScope {
109}109}
110110
111111
112
113
114export class SlashCommandScopeVariableExistsError extends Error {}112export class SlashCommandScopeVariableExistsError extends Error {}
115113
116114
public/scripts/tags.js+0 -1
@@ -2091,7 +2091,6 @@ function onTagAsFolderClick() {
2091 // If folder display has changed, we have to redraw the character list, otherwise this folders state would not change2091 // If folder display has changed, we have to redraw the character list, otherwise this folders state would not change
2092 printCharactersDebounced();2092 printCharactersDebounced();
2093 saveSettingsDebounced();2093 saveSettingsDebounced();
2094
2095}2094}
20962095
2097function updateDrawTagFolder(element, tag) {2096function updateDrawTagFolder(element, tag) {
public/scripts/textgen-settings.js+2 -3
@@ -802,7 +802,6 @@ async function getStatusTextgen() {
802 console.info('Status check aborted.', err.reason);802 console.info('Status check aborted.', err.reason);
803 } else {803 } else {
804 console.error('Error getting status', err);804 console.error('Error getting status', err);
805
806 }805 }
807 setOnlineStatus('no_connection');806 setOnlineStatus('no_connection');
808 }807 }
@@ -1126,7 +1125,7 @@ export function initTextGenSettings() {
1126 * @returns void1125 * @returns void
1127 */1126 */
1128function showSamplerControls(apiType = null) {1127function showSamplerControls(apiType = null) {
1129 $('#textgenerationwebui_api-settings [data-tg-samplers], #textgenerationwebui_api [data-tg-samplers]').each(function(idx, elem) {1128 $('#textgenerationwebui_api-settings [data-tg-samplers], #textgenerationwebui_api [data-tg-samplers]').each(function (idx, elem) {
1130 const typeSpecificControlled = $(elem).data('tg-type') !== undefined;1129 const typeSpecificControlled = $(elem).data('tg-type') !== undefined;
11311130
1132 if (!typeSpecificControlled) $(this).show();1131 if (!typeSpecificControlled) $(this).show();
@@ -1139,7 +1138,7 @@ function showSamplerControls(apiType = null) {
11391138
1140 if (!samplersActivatedManually?.length || !prioritizeManualSamplerSelect) return;1139 if (!samplersActivatedManually?.length || !prioritizeManualSamplerSelect) return;
11411140
1142 $('#textgenerationwebui_api-settings [data-tg-samplers], #textgenerationwebui_api [data-tg-samplers]').each(function() {1141 $('#textgenerationwebui_api-settings [data-tg-samplers], #textgenerationwebui_api [data-tg-samplers]').each(function () {
1143 const tgSamplers = $(this).attr('data-tg-samplers').split(',').map(x => x.trim()).filter(str => str !== '');1142 const tgSamplers = $(this).attr('data-tg-samplers').split(',').map(x => x.trim()).filter(str => str !== '');
11441143
1145 for (const tgSampler of tgSamplers) {1144 for (const tgSampler of tgSamplers) {
public/scripts/user.js+0 -4
@@ -455,7 +455,6 @@ async function changeName(handle, name, callback) {
455455
456 toastr.success('Name changed successfully', 'Name Changed');456 toastr.success('Name changed successfully', 'Name Changed');
457 callback();457 callback();
458
459 } catch (error) {458 } catch (error) {
460 console.error('Error changing name:', error);459 console.error('Error changing name:', error);
461 }460 }
@@ -495,7 +494,6 @@ async function restoreSnapshot(name, callback) {
495 } catch (error) {494 } catch (error) {
496 console.error('Error restoring snapshot:', error);495 console.error('Error restoring snapshot:', error);
497 }496 }
498
499}497}
500498
501/**499/**
@@ -601,7 +599,6 @@ async function viewSettingsSnapshots() {
601 const content = await loadSnapshotContent(snapshot.name);599 const content = await loadSnapshotContent(snapshot.name);
602 contentBlock.val(content);600 contentBlock.val(content);
603 }601 }
604
605 });602 });
606 template.find('.snapshotList').append(snapshotBlock);603 template.find('.snapshotList').append(snapshotBlock);
607 }604 }
@@ -667,7 +664,6 @@ async function resetEverything(callback) {
667 } catch (error) {664 } catch (error) {
668 console.error('Error resetting everything:', error);665 console.error('Error resetting everything:', error);
669 }666 }
670
671}667}
672668
673async function openUserProfile() {669async function openUserProfile() {
public/scripts/utils.js+0 -1
@@ -2310,7 +2310,6 @@ export function highlightRegex(regexStr) {
2310 flags: new RegExp('(?<=\\/)([gimsuy]*)$', 'g'), // Match trailing flags2310 flags: new RegExp('(?<=\\/)([gimsuy]*)$', 'g'), // Match trailing flags
2311 delimiters: new RegExp('^\\/|(?<![\\\\<])\\/', 'g'), // Match leading or trailing delimiters2311 delimiters: new RegExp('^\\/|(?<![\\\\<])\\/', 'g'), // Match leading or trailing delimiters
2312 };2312 };
2313
2314 } catch (error) {2313 } catch (error) {
2315 return {2314 return {
2316 brackets: new RegExp('(\\\\)?\\[.*?\\]', 'g'), // Non-escaped square brackets2315 brackets: new RegExp('(\\\\)?\\[.*?\\]', 'g'), // Non-escaped square brackets
public/scripts/world-info.js+0 -2
@@ -675,7 +675,6 @@ class WorldInfoTimedEffects {
675 console.log('[WI] Timed effect "delay" applied to entry', entry);675 console.log('[WI] Timed effect "delay" applied to entry', entry);
676 }676 }
677 }677 }
678
679 }678 }
680679
681 /**680 /**
@@ -4495,7 +4494,6 @@ function parseDecorators(content) {
4495 }4494 }
44964495
4497 return [[], content];4496 return [[], content];
4498
4499}4497}
45004498
4501/**4499/**
src/endpoints/assets.js+0 -2
@@ -111,7 +111,6 @@ router.post('/get', async (request, response) => {
111111
112 try {112 try {
113 if (fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory()) {113 if (fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory()) {
114
115 ensureFoldersExist(request.user.directories);114 ensureFoldersExist(request.user.directories);
116115
117 const folders = fs.readdirSync(folderPath, { withFileTypes: true })116 const folders = fs.readdirSync(folderPath, { withFileTypes: true })
@@ -346,7 +345,6 @@ router.post('/character', async (request, response) => {
346 let output = [];345 let output = [];
347 try {346 try {
348 if (fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory()) {347 if (fs.existsSync(folderPath) && fs.statSync(folderPath).isDirectory()) {
349
350 // Live2d assets348 // Live2d assets
351 if (category == 'live2d') {349 if (category == 'live2d') {
352 const folders = fs.readdirSync(folderPath, { withFileTypes: true });350 const folders = fs.readdirSync(folderPath, { withFileTypes: true });
src/endpoints/backends/text-completions.js+0 -2
@@ -541,7 +541,6 @@ llamacpp.post('/props', async function (request, response) {
541 console.debug('LlamaCpp props response:', data);541 console.debug('LlamaCpp props response:', data);
542542
543 return response.send(data);543 return response.send(data);
544
545 } catch (error) {544 } catch (error) {
546 console.error(error);545 console.error(error);
547 return response.sendStatus(500);546 return response.sendStatus(500);
@@ -591,7 +590,6 @@ llamacpp.post('/slots', async function (request, response) {
591 console.debug('LlamaCpp slots response:', data);590 console.debug('LlamaCpp slots response:', data);
592591
593 return response.send(data);592 return response.send(data);
594
595 } catch (error) {593 } catch (error) {
596 console.error(error);594 console.error(error);
597 return response.sendStatus(500);595 return response.sendStatus(500);
src/endpoints/backups.js+1 -1
@@ -11,7 +11,7 @@ router.post('/chat/get', async (request, response) => {
11 const backupModels = [];11 const backupModels = [];
12 const backupFiles = await fsPromises12 const backupFiles = await fsPromises
13 .readdir(request.user.directories.backups, { withFileTypes: true })13 .readdir(request.user.directories.backups, { withFileTypes: true })
14 .then(d => d .filter(d => d.isFile() && path.extname(d.name) === '.jsonl' && d.name.startsWith(CHAT_BACKUPS_PREFIX)).map(d => d.name));14 .then(d => d.filter(d => d.isFile() && path.extname(d.name) === '.jsonl' && d.name.startsWith(CHAT_BACKUPS_PREFIX)).map(d => d.name));
1515
16 for (const name of backupFiles) {16 for (const name of backupFiles) {
17 const filePath = path.join(request.user.directories.backups, name);17 const filePath = path.join(request.user.directories.backups, name);
src/endpoints/characters.js+2 -2
@@ -639,7 +639,6 @@ function charaFormatData(data, directories) {
639 if (file && file.entries) {639 if (file && file.entries) {
640 _.set(char, 'data.character_book', convertWorldInfoToCharacterBook(data.world, file.entries));640 _.set(char, 'data.character_book', convertWorldInfoToCharacterBook(data.world, file.entries));
641 }641 }
642
643 } catch {642 } catch {
644 console.warn(`Failed to read world info file: ${data.world}. Character book will not be available.`);643 console.warn(`Failed to read world info file: ${data.world}. Character book will not be available.`);
645 }644 }
@@ -921,7 +920,8 @@ async function importFromJson(uploadPath, { request }, preservedFileName) {
921 let charJSON = JSON.stringify(char);920 let charJSON = JSON.stringify(char);
922 const result = await writeCharacterData(DEFAULT_AVATAR_PATH, charJSON, pngName, request);921 const result = await writeCharacterData(DEFAULT_AVATAR_PATH, charJSON, pngName, request);
923 return result ? pngName : '';922 return result ? pngName : '';
924 } else if (jsonData.char_name !== undefined) {//json Pygmalion notepad923 } else if (jsonData.char_name !== undefined) {
924 //json Pygmalion notepad
925 console.info('Importing from gradio json');925 console.info('Importing from gradio json');
926 jsonData.char_name = sanitize(jsonData.char_name);926 jsonData.char_name = sanitize(jsonData.char_name);
927 if (jsonData.creator_notes) {927 if (jsonData.creator_notes) {
src/endpoints/extensions.js+0 -2
@@ -367,7 +367,6 @@ router.post('/version', async (request, response) => {
367 const { isUpToDate, remoteUrl } = await checkIfRepoIsUpToDate(extensionPath);367 const { isUpToDate, remoteUrl } = await checkIfRepoIsUpToDate(extensionPath);
368368
369 return response.send({ currentBranchName, currentCommitHash, isUpToDate, remoteUrl });369 return response.send({ currentBranchName, currentCommitHash, isUpToDate, remoteUrl });
370
371 } catch (error) {370 } catch (error) {
372 console.error('Getting extension version failed', error);371 console.error('Getting extension version failed', error);
373 return response.status(500).send(`Server Error: ${error.message}`);372 return response.status(500).send(`Server Error: ${error.message}`);
@@ -406,7 +405,6 @@ router.post('/delete', async (request, response) => {
406 console.info(`Extension has been deleted at ${extensionPath}`);405 console.info(`Extension has been deleted at ${extensionPath}`);
407406
408 return response.send(`Extension has been deleted at ${extensionPath}`);407 return response.send(`Extension has been deleted at ${extensionPath}`);
409
410 } catch (error) {408 } catch (error) {
411 console.error('Deleting custom content failed', error);409 console.error('Deleting custom content failed', error);
412 return response.status(500).send(`Server Error: ${error.message}`);410 return response.status(500).send(`Server Error: ${error.message}`);
src/endpoints/groups.js+1 -1
@@ -51,7 +51,7 @@ export async function migrateGroupChatsMetadataFormat(userDirectories) {
51 if (!needsMigration) {51 if (!needsMigration) {
52 continue;52 continue;
53 }53 }
54 if (!fs.existsSync(backupPath)){54 if (!fs.existsSync(backupPath)) {
55 await fsPromises.mkdir(backupPath, { recursive: true });55 await fsPromises.mkdir(backupPath, { recursive: true });
56 }56 }
57 await fsPromises.copyFile(groupFilePath, path.join(backupPath, groupFile.name));57 await fsPromises.copyFile(groupFilePath, path.join(backupPath, groupFile.name));
src/endpoints/horde.js+0 -2
@@ -256,7 +256,6 @@ router.post('/caption-image', async (request, response) => {
256 console.info(status);256 console.info(status);
257257
258 if (status.state === HordeAsyncRequestStates.done) {258 if (status.state === HordeAsyncRequestStates.done) {
259
260 if (status.forms === undefined) {259 if (status.forms === undefined) {
261 console.error('Image interrogation request failed: no forms found.');260 console.error('Image interrogation request failed: no forms found.');
262 return response.sendStatus(500);261 return response.sendStatus(500);
@@ -278,7 +277,6 @@ router.post('/caption-image', async (request, response) => {
278 return response.sendStatus(503);277 return response.sendStatus(503);
279 }278 }
280 }279 }
281
282 } catch (error) {280 } catch (error) {
283 console.error(error);281 console.error(error);
284 response.sendStatus(500);282 response.sendStatus(500);
src/endpoints/image-metadata.js+0 -1
@@ -442,7 +442,6 @@ router.post('/', async function (request, response) {
442 }442 }
443443
444 return response.status(400).json({ error: 'Invalid request format.' });444 return response.status(400).json({ error: 'Invalid request format.' });
445
446 } catch (error) {445 } catch (error) {
447 console.error('[ImageMetadata] API error:', error);446 console.error('[ImageMetadata] API error:', error);
448 return response.status(500).json({ error: 'Internal server error.' });447 return response.status(500).json({ error: 'Internal server error.' });
src/endpoints/minimax.js+0 -2
@@ -189,7 +189,6 @@ router.post('/generate-voice', async (request, response) => {
189 response.setHeader('Content-Length', audioBytes.length);189 response.setHeader('Content-Length', audioBytes.length);
190190
191 return response.send(Buffer.from(audioBytes));191 return response.send(Buffer.from(audioBytes));
192
193 } catch (conversionError) {192 } catch (conversionError) {
194 console.error('MiniMax TTS: Audio conversion error:', conversionError);193 console.error('MiniMax TTS: Audio conversion error:', conversionError);
195 return response.status(500).json({ error: `Audio data conversion failed: ${conversionError.message}` });194 return response.status(500).json({ error: `Audio data conversion failed: ${conversionError.message}` });
@@ -222,7 +221,6 @@ router.post('/generate-voice', async (request, response) => {
222 console.error('MiniMax TTS: No valid audio data in response:', responseData);221 console.error('MiniMax TTS: No valid audio data in response:', responseData);
223 return response.status(500).json({ error: `API Error: ${errorMessage}` });222 return response.status(500).json({ error: `API Error: ${errorMessage}` });
224 }223 }
225
226 } catch (error) {224 } catch (error) {
227 console.error('MiniMax TTS generation failed:', error);225 console.error('MiniMax TTS generation failed:', error);
228 return response.status(500).json({ error: 'Internal server error' });226 return response.status(500).json({ error: 'Internal server error' });
src/endpoints/stable-diffusion.js+0 -1
@@ -158,7 +158,6 @@ router.post('/samplers', async (request, response) => {
158 const data = await result.json();158 const data = await result.json();
159 const names = data.map(x => x.name);159 const names = data.map(x => x.name);
160 return response.send(names);160 return response.send(names);
161
162 } catch (error) {161 } catch (error) {
163 console.error(error);162 console.error(error);
164 return response.sendStatus(500);163 return response.sendStatus(500);
src/endpoints/thumbnails.js+0 -1
@@ -301,7 +301,6 @@ publicRouter.get('/', async function (request, response) {
301301
302 // Send a 404 so the frontend can display a placeholder302 // Send a 404 so the frontend can display a placeholder
303 return response.sendStatus(404);303 return response.sendStatus(404);
304
305 } catch (error) {304 } catch (error) {
306 console.error('Failed getting thumbnail', error);305 console.error('Failed getting thumbnail', error);
307 return response.sendStatus(500);306 return response.sendStatus(500);
src/prompt-converters.js+1 -2
@@ -118,7 +118,6 @@ export function postProcessPrompt(messages, type, names) {
118 * @copyright Prompt Conversion script taken from RisuAI by kwaroran (GPLv3).118 * @copyright Prompt Conversion script taken from RisuAI by kwaroran (GPLv3).
119 */119 */
120export function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill, withSysPromptSupport, useSystemPrompt, addSysHumanMsg, excludePrefixes) {120export function convertClaudePrompt(messages, addAssistantPostfix, addAssistantPrefill, withSysPromptSupport, useSystemPrompt, addSysHumanMsg, excludePrefixes) {
121
122 //Prepare messages for claude.121 //Prepare messages for claude.
123 //When 'Exclude Human/Assistant prefixes' checked, setting messages role to the 'system'(last message is exception).122 //When 'Exclude Human/Assistant prefixes' checked, setting messages role to the 'system'(last message is exception).
124 if (messages.length > 0) {123 if (messages.length > 0) {
@@ -1278,7 +1277,7 @@ export function calculateGoogleBudgetTokens(maxTokens, reasoningEffort, model) {
1278 return getGemini3ProBudget();1277 return getGemini3ProBudget();
1279 }1278 }
12801279
1281 if (/gemini-3-flash/.test(model) ) {1280 if (/gemini-3-flash/.test(model)) {
1282 return getGemini3FlashBudget();1281 return getGemini3FlashBudget();
1283 }1282 }
12841283
src/util.js+0 -1
@@ -1012,7 +1012,6 @@ export async function canResolve(name, useIPv6 = true, useIPv4 = true) {
1012 }1012 }
10131013
1014 return v6Resolved || v4Resolved;1014 return v6Resolved || v4Resolved;
1015
1016 } catch (error) {1015 } catch (error) {
1017 return false;1016 return false;
1018 }1017 }