"gradually replacing property access with a dot operator" (#4965) * "gradually replacing property access with a dot operator" https://github.com/SillyTavern/SillyTavern/pull/4963#discussion_r2663003561 (?<=\w|\])\['([a-zA-Z]\w+)'\] My regex found 593 matches across 47 files. Also, two typos. * Fixed chat[0].chat_metadata type error. https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664275854 * Fixed `swipedElementsDiv[0]?.getAnimations().filter((a) => a.animationName` type error. https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664274593 * Fixed config.MESSAGE_SANITIZE and config.MESSAGE_ALLOW_SYSTEM_UI type errors. https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664266271 * Fixed group.date_last_chat type error. https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664295652 * Reverted SlashCommandParser dot property access. https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664310931 * LLM fixed canUseNegativeLookbehind.result; type error. https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664314288 * Reverted chat-completions.js bodyParams and headers dot property access. https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664317848 https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664320088 https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664324438 * Reverted openai.js data dot property access. https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664326244 * Reverted tests/frontend/MacroEnvBuilder.e2e.js env.dynamicMacros dot property access. https://github.com/SillyTavern/SillyTavern/pull/4965#discussion_r2664330990 * Partially reverted `window` dot property access. * Reverted result.json() and settings dot property access. * Reverted google.js headers dot property access. * Fixed regex: `(?<=\w|\])\['([a-zA-Z]\w*)'\]` * Swapped window to globalThis with dot property access. * LLM fixed canUseNegativeLookbehind type. * Refactor property access * Consistency --------- Co-authored-by: user <user@exmaple.com> Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

8372e7bf9df1ff8e2df9f15765cd6f96d8689818

DeclineThyself <235079501+DeclineThyself@users.noreply.github.com>

Signed
44 files changed, +463 -443Ignore whitespace
public/global.d.ts+9 -0
@@ -38,6 +38,7 @@ declare global {
38 avatar_url?: string;38 avatar_url?: string;
39 hideMutedSprites?: boolean;39 hideMutedSprites?: boolean;
40 fav?: boolean;40 fav?: boolean;
41 date_last_chat?: MessageTimestamp;
41 }42 }
4243
43 interface ChatFile extends Array<ChatMessage> {44 interface ChatFile extends Array<ChatMessage> {
@@ -235,3 +236,11 @@ declare global {
235236
236 type SwipeEvent = JQuery.TriggeredEvent<any, any, HTMLElement, HTMLElement>;237 type SwipeEvent = JQuery.TriggeredEvent<any, any, HTMLElement, HTMLElement>;
237}238}
239
240//Overrides for public/scripts/chats.js
241declare module 'dompurify' {
242 interface Config {
243 MESSAGE_SANITIZE?: boolean;
244 MESSAGE_ALLOW_SYSTEM_UI?: boolean;
245 }
246}
public/script.js+198 -193
@@ -1176,8 +1176,8 @@ export async function getOneCharacter(avatarUrl) {
11761176
1177 if (response.ok) {1177 if (response.ok) {
1178 const getData = await response.json();1178 const getData = await response.json();
1179 getData['name'] = DOMPurify.sanitize(getData['name']);1179 getData.name = DOMPurify.sanitize(getData.name);
1180 getData['chat'] = String(getData['chat']);1180 getData.chat = String(getData.chat);
11811181
1182 const indexOf = characters.findIndex(x => x.avatar === avatarUrl);1182 const indexOf = characters.findIndex(x => x.avatar === avatarUrl);
11831183
@@ -1248,14 +1248,14 @@ export async function getCharacters() {
1248 const getData = await response.json();1248 const getData = await response.json();
1249 for (let i = 0; i < getData.length; i++) {1249 for (let i = 0; i < getData.length; i++) {
1250 characters[i] = getData[i];1250 characters[i] = getData[i];
1251 characters[i]['name'] = DOMPurify.sanitize(characters[i]['name']);1251 characters[i].name = DOMPurify.sanitize(characters[i].name);
12521252
1253 // For dropped-in cards1253 // For dropped-in cards
1254 if (!characters[i]['chat']) {1254 if (!characters[i].chat) {
1255 characters[i]['chat'] = `${characters[i]['name']} - ${humanizedDateTime()}`;1255 characters[i].chat = `${characters[i].name} - ${humanizedDateTime()}`;
1256 }1256 }
12571257
1258 characters[i]['chat'] = String(characters[i]['chat']);1258 characters[i].chat = String(characters[i].chat);
1259 }1259 }
12601260
1261 if (previousAvatar) {1261 if (previousAvatar) {
@@ -1555,7 +1555,7 @@ export async function deleteMessage(id, swipeDeletionIndex = undefined, askConfi
1555 chat.splice(id, 1);1555 chat.splice(id, 1);
1556 messageElement.remove();1556 messageElement.remove();
15571557
1558 chat_metadata['tainted'] = true;1558 chat_metadata.tainted = true;
15591559
1560 const startIndex = [0, minId].includes(id) ? id : null;1560 const startIndex = [0, minId].includes(id) ? id : null;
1561 updateViewMessageIds(startIndex);1561 updateViewMessageIds(startIndex);
@@ -1625,8 +1625,8 @@ export async function sendTextareaMessage() {
1625 !textareaText &&1625 !textareaText &&
1626 !selected_group &&1626 !selected_group &&
1627 chat.length &&1627 chat.length &&
1628 !lastMessage['is_user'] &&1628 !lastMessage.is_user &&
1629 !lastMessage['is_system']1629 !lastMessage.is_system
1630 ) {1630 ) {
1631 generateType = 'continue';1631 generateType = 'continue';
1632 }1632 }
@@ -2406,7 +2406,7 @@ export function addCopyToCodeBlocks(messageElement) {
2406 * @returns {void}2406 * @returns {void}
2407 */2407 */
2408export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll = true, insertBefore = null, forceId = null, showSwipes = true } = {}) {2408export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll = true, insertBefore = null, forceId = null, showSwipes = true } = {}) {
2409 let messageText = mes['mes'];2409 let messageText = mes.mes;
2410 const momentDate = timestampToMoment(mes.send_date);2410 const momentDate = timestampToMoment(mes.send_date);
2411 const timestamp = momentDate.isValid() ? momentDate.format('LL LT') : '';2411 const timestamp = momentDate.isValid() ? momentDate.format('LL LT') : '';
24122412
@@ -2425,8 +2425,8 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
2425 const isSystem = mes.is_system;2425 const isSystem = mes.is_system;
2426 const title = mes.title;2426 const title = mes.title;
24272427
2428 //for non-user mesages2428 //for non-user messages
2429 if (!mes['is_user']) {2429 if (!mes.is_user) {
2430 if (mes.force_avatar) {2430 if (mes.force_avatar) {
2431 avatarImg = mes.force_avatar;2431 avatarImg = mes.force_avatar;
2432 } else if (this_chid === undefined) {2432 } else if (this_chid === undefined) {
@@ -2442,9 +2442,9 @@ export function addOneMessage(mes, { type = 'normal', insertAfter = null, scroll
2442 //if messge is from sytem, use the name provided in the message JSONL to proceed,2442 //if messge is from sytem, use the name provided in the message JSONL to proceed,
2443 //if not system message, use name2 (char's name) to proceed2443 //if not system message, use name2 (char's name) to proceed
2444 //characterName = mes.is_system || mes.force_avatar ? mes.name : name2;2444 //characterName = mes.is_system || mes.force_avatar ? mes.name : name2;
2445 } else if (mes['is_user'] && mes['force_avatar']) {2445 } else if (mes.is_user && mes.force_avatar) {
2446 // Special case for persona images.2446 // Special case for persona images.
2447 avatarImg = mes['force_avatar'];2447 avatarImg = mes.force_avatar;
2448 }2448 }
24492449
2450 // if mes.extra.uses_system_ui is true, set an override on the sanitizer options2450 // if mes.extra.uses_system_ui is true, set an override on the sanitizer options
@@ -3265,7 +3265,7 @@ export function getCharacterCardFieldsLazy({ chid = undefined } = {}) {
3265 persona: () => baseChatReplace(power_user.persona_description?.trim()),3265 persona: () => baseChatReplace(power_user.persona_description?.trim()),
3266 system: () => {3266 system: () => {
3267 if (!character) return '';3267 if (!character) return '';
3268 const systemPrompt = chat_metadata['system_prompt'] || character.data?.system_prompt || '';3268 const systemPrompt = chat_metadata.system_prompt || character.data?.system_prompt || '';
3269 return power_user.prefer_character_prompt ? baseChatReplace(systemPrompt.trim()) : '';3269 return power_user.prefer_character_prompt ? baseChatReplace(systemPrompt.trim()) : '';
3270 },3270 },
3271 jailbreak: () => {3271 jailbreak: () => {
@@ -3295,13 +3295,13 @@ export function getCharacterCardFieldsLazy({ chid = undefined } = {}) {
3295 scenario: () => {3295 scenario: () => {
3296 if (groupCardsLazy) return groupCardsLazy.scenario;3296 if (groupCardsLazy) return groupCardsLazy.scenario;
3297 if (!character) return '';3297 if (!character) return '';
3298 const scenarioText = chat_metadata['scenario'] || character.scenario || '';3298 const scenarioText = chat_metadata.scenario || character.scenario || '';
3299 return baseChatReplace(scenarioText.trim());3299 return baseChatReplace(scenarioText.trim());
3300 },3300 },
3301 mesExamples: () => {3301 mesExamples: () => {
3302 if (groupCardsLazy) return groupCardsLazy.mesExamples;3302 if (groupCardsLazy) return groupCardsLazy.mesExamples;
3303 if (!character) return '';3303 if (!character) return '';
3304 const exampleDialog = chat_metadata['mes_example'] || character.mes_example || '';3304 const exampleDialog = chat_metadata.mes_example || character.mes_example || '';
3305 return baseChatReplace(exampleDialog.trim());3305 return baseChatReplace(exampleDialog.trim());
3306 },3306 },
3307 };3307 };
@@ -3516,39 +3516,39 @@ class StreamingProcessor {
3516 this.sendTextarea.value = processedText;3516 this.sendTextarea.value = processedText;
3517 this.sendTextarea.dispatchEvent(new Event('input', { bubbles: true }));3517 this.sendTextarea.dispatchEvent(new Event('input', { bubbles: true }));
3518 } else {3518 } else {
3519 const mesChanged = chat[messageId]['mes'] !== processedText;3519 const mesChanged = chat[messageId].mes !== processedText;
3520 await this.#checkDomElements(messageId);3520 await this.#checkDomElements(messageId);
3521 this.#updateMessageBlockVisibility();3521 this.#updateMessageBlockVisibility();
3522 const currentTime = new Date();3522 const currentTime = new Date();
3523 chat[messageId]['mes'] = processedText;3523 chat[messageId].mes = processedText;
3524 chat[messageId]['gen_started'] = this.timeStarted;3524 chat[messageId].gen_started = this.timeStarted;
3525 chat[messageId]['gen_finished'] = currentTime;3525 chat[messageId].gen_finished = currentTime;
3526 if (!chat[messageId]['extra']) {3526 if (!chat[messageId].extra) {
3527 chat[messageId]['extra'] = {};3527 chat[messageId].extra = {};
3528 }3528 }
3529 chat[messageId]['extra']['time_to_first_token'] = this.timeToFirstToken;3529 chat[messageId].extra.time_to_first_token = this.timeToFirstToken;
35303530
3531 // Update reasoning3531 // Update reasoning
3532 await this.reasoningHandler.process(messageId, mesChanged, this.promptReasoning);3532 await this.reasoningHandler.process(messageId, mesChanged, this.promptReasoning);
3533 processedText = chat[messageId]['mes'];3533 processedText = chat[messageId].mes;
35343534
3535 // Token count update.3535 // Token count update.
3536 const tokenCountText = this.reasoningHandler.reasoning + processedText;3536 const tokenCountText = this.reasoningHandler.reasoning + processedText;
3537 const currentTokenCount = isFinal && power_user.message_token_count_enabled ? await getTokenCountAsync(tokenCountText, 0) : 0;3537 const currentTokenCount = isFinal && power_user.message_token_count_enabled ? await getTokenCountAsync(tokenCountText, 0) : 0;
3538 if (currentTokenCount) {3538 if (currentTokenCount) {
3539 chat[messageId]['extra']['token_count'] = currentTokenCount;3539 chat[messageId].extra.token_count = currentTokenCount;
3540 if (this.messageTokenCounterDom instanceof HTMLElement) {3540 if (this.messageTokenCounterDom instanceof HTMLElement) {
3541 this.messageTokenCounterDom.textContent = `${currentTokenCount}t`;3541 this.messageTokenCounterDom.textContent = `${currentTokenCount}t`;
3542 }3542 }
3543 }3543 }
35443544
3545 if ((this.type == 'swipe' || this.type === 'continue') && Array.isArray(chat[messageId]['swipes'])) {3545 if ((this.type == 'swipe' || this.type === 'continue') && Array.isArray(chat[messageId].swipes)) {
3546 chat[messageId]['swipes'][chat[messageId]['swipe_id']] = processedText;3546 chat[messageId].swipes[chat[messageId].swipe_id] = processedText;
3547 chat[messageId]['swipe_info'][chat[messageId]['swipe_id']] = {3547 chat[messageId].swipe_info[chat[messageId].swipe_id] = {
3548 'send_date': chat[messageId]['send_date'],3548 'send_date': chat[messageId].send_date,
3549 'gen_started': chat[messageId]['gen_started'],3549 'gen_started': chat[messageId].gen_started,
3550 'gen_finished': chat[messageId]['gen_finished'],3550 'gen_finished': chat[messageId].gen_finished,
3551 'extra': structuredClone(chat[messageId]['extra']),3551 'extra': structuredClone(chat[messageId].extra),
3552 };3552 };
3553 }3553 }
35543554
@@ -3657,13 +3657,13 @@ class StreamingProcessor {
36573657
3658 setFirstSwipe(messageId) {3658 setFirstSwipe(messageId) {
3659 if (this.type !== 'swipe' && this.type !== 'impersonate') {3659 if (this.type !== 'swipe' && this.type !== 'impersonate') {
3660 if (Array.isArray(chat[messageId]['swipes']) && chat[messageId]['swipes'].length === 1 && chat[messageId]['swipe_id'] === 0) {3660 if (Array.isArray(chat[messageId].swipes) && chat[messageId].swipes.length === 1 && chat[messageId].swipe_id === 0) {
3661 chat[messageId]['swipes'][0] = chat[messageId]['mes'];3661 chat[messageId].swipes[0] = chat[messageId].mes;
3662 chat[messageId]['swipe_info'][0] = {3662 chat[messageId].swipe_info[0] = {
3663 'send_date': chat[messageId]['send_date'],3663 'send_date': chat[messageId].send_date,
3664 'gen_started': chat[messageId]['gen_started'],3664 'gen_started': chat[messageId].gen_started,
3665 'gen_finished': chat[messageId]['gen_finished'],3665 'gen_finished': chat[messageId].gen_finished,
3666 'extra': structuredClone(chat[messageId]['extra']),3666 'extra': structuredClone(chat[messageId].extra),
3667 };3667 };
3668 }3668 }
3669 }3669 }
@@ -4143,7 +4143,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4143 // Hide swipes if not in a dry run.4143 // Hide swipes if not in a dry run.
4144 hideSwipeButtons();4144 hideSwipeButtons();
4145 // If generated any message, set the flag to indicate it can't be recreated again.4145 // If generated any message, set the flag to indicate it can't be recreated again.
4146 chat_metadata['tainted'] = true;4146 chat_metadata.tainted = true;
4147 }4147 }
41484148
4149 if (selected_group && !is_group_generating) {4149 if (selected_group && !is_group_generating) {
@@ -4201,7 +4201,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4201 $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true }));4201 $('#send_textarea').val('')[0].dispatchEvent(new Event('input', { bubbles: true }));
4202 } else {4202 } else {
4203 textareaText = '';4203 textareaText = '';
4204 if (chat.length && lastMessage['is_user']) {4204 if (chat.length && lastMessage.is_user) {
4205 //do nothing? why does this check exist?4205 //do nothing? why does this check exist?
4206 }4206 }
4207 else if (type !== 'quiet' && type !== 'swipe' && !isImpersonate && !dryRun && chat.length) {4207 else if (type !== 'quiet' && type !== 'swipe' && !isImpersonate && !dryRun && chat.length) {
@@ -4215,13 +4215,13 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
42154215
4216 // Rewrite the generation timer to account for the time passed for all the continuations.4216 // Rewrite the generation timer to account for the time passed for all the continuations.
4217 if (isContinue && chat.length) {4217 if (isContinue && chat.length) {
4218 const prevFinished = lastMessage['gen_finished'];4218 const prevFinished = lastMessage.gen_finished;
4219 const prevStarted = lastMessage['gen_started'];4219 const prevStarted = lastMessage.gen_started;
42204220
4221 if (prevFinished && prevStarted) {4221 if (prevFinished && prevStarted) {
4222 const timePassed = Number(prevFinished) - Number(prevStarted);4222 const timePassed = Number(prevFinished) - Number(prevStarted);
4223 generation_started = new Date(Date.now() - timePassed);4223 generation_started = new Date(Date.now() - timePassed);
4224 lastMessage['gen_started'] = generation_started;4224 lastMessage.gen_started = generation_started;
4225 }4225 }
4226 }4226 }
42274227
@@ -4903,7 +4903,7 @@ export async function Generate(type, { automatic_trigger, force_name2, quiet_pro
4903 let thisPromptContextSize = await getTokenCountAsync(prompt, power_user.token_padding);4903 let thisPromptContextSize = await getTokenCountAsync(prompt, power_user.token_padding);
49044904
4905 if (thisPromptContextSize > this_max_context) { //if the prepared prompt is larger than the max context size...4905 if (thisPromptContextSize > this_max_context) { //if the prepared prompt is larger than the max context size...
4906 if (count_exm_add > 0) { // ..and we have example mesages..4906 if (count_exm_add > 0) { // ..and we have example messages..
4907 count_exm_add--; // remove the example messages...4907 count_exm_add--; // remove the example messages...
4908 await checkPromptSize(); // and try agin...4908 await checkPromptSize(); // and try agin...
4909 } else if (mesSend.length > 0) { // if the chat history is longer than 04909 } else if (mesSend.length > 0) { // if the chat history is longer than 0
@@ -5631,7 +5631,7 @@ export function getBiasStrings(textareaText, type) {
5631function formatMessageHistoryItem(chatItem, isInstruct, forceOutputSequence) {5631function formatMessageHistoryItem(chatItem, isInstruct, forceOutputSequence) {
5632 const isNarratorType = chatItem?.extra?.type === system_message_types.NARRATOR;5632 const isNarratorType = chatItem?.extra?.type === system_message_types.NARRATOR;
5633 const characterName = chatItem?.name ? chatItem.name : name2;5633 const characterName = chatItem?.name ? chatItem.name : name2;
5634 const itemName = chatItem.is_user ? chatItem['name'] : characterName;5634 const itemName = chatItem.is_user ? chatItem.name : characterName;
5635 const shouldPrependName = !isNarratorType;5635 const shouldPrependName = !isNarratorType;
56365636
5637 // If this symbol flag is set, completely ignore the message.5637 // If this symbol flag is set, completely ignore the message.
@@ -5700,7 +5700,7 @@ export async function sendMessageAsUser(messageText, messageBias, insertAt = nul
5700 await populateFileAttachment(message);5700 await populateFileAttachment(message);
5701 statMesProcess(message, 'user', characters, this_chid, '');5701 statMesProcess(message, 'user', characters, this_chid, '');
57025702
5703 chat_metadata['tainted'] = true;5703 chat_metadata.tainted = true;
57045704
5705 if (typeof insertAt === 'number' && insertAt >= 0 && insertAt <= chat.length) {5705 if (typeof insertAt === 'number' && insertAt >= 0 && insertAt <= chat.length) {
5706 chat.splice(insertAt, 0, message);5706 chat.splice(insertAt, 0, message);
@@ -5851,7 +5851,7 @@ function setInContextMessages(msgInContextCount, type) {
58515851
5852 // Update last id to chat. No metadata save on purpose, gets hopefully saved via another call5852 // Update last id to chat. No metadata save on purpose, gets hopefully saved via another call
5853 const lastMessageId = Math.max(0, chat.length - msgInContextCount);5853 const lastMessageId = Math.max(0, chat.length - msgInContextCount);
5854 chat_metadata['lastInContextMessageId'] = lastMessageId;5854 chat_metadata.lastInContextMessageId = lastMessageId;
5855}5855}
58565856
5857/**5857/**
@@ -6392,18 +6392,18 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title
63926392
6393 const lastMessage = chat[chat.length - 1];6393 const lastMessage = chat[chat.length - 1];
63946394
6395 if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (lastMessage['swipe_id'] === undefined ||6395 if (type != 'append' && type != 'continue' && type != 'appendFinal' && chat.length && (lastMessage.swipe_id === undefined ||
6396 lastMessage['is_user'])) {6396 lastMessage.is_user)) {
6397 type = 'normal';6397 type = 'normal';
6398 }6398 }
63996399
6400 if (chat.length && (!lastMessage['extra'] || typeof lastMessage['extra'] !== 'object')) {6400 if (chat.length && (!lastMessage.extra || typeof lastMessage.extra !== 'object')) {
6401 lastMessage['extra'] = {};6401 lastMessage.extra = {};
6402 }6402 }
64036403
6404 // Coerce null/undefined to empty string6404 // Coerce null/undefined to empty string
6405 if (chat.length && !lastMessage['extra']['reasoning']) {6405 if (chat.length && !lastMessage.extra.reasoning) {
6406 lastMessage['extra']['reasoning'] = '';6406 lastMessage.extra.reasoning = '';
6407 }6407 }
64086408
6409 if (!reasoning) {6409 if (!reasoning) {
@@ -6413,70 +6413,70 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title
6413 let oldMessage = '';6413 let oldMessage = '';
6414 const generationFinished = new Date();6414 const generationFinished = new Date();
6415 if (type === 'swipe') {6415 if (type === 'swipe') {
6416 oldMessage = lastMessage['mes'];6416 oldMessage = lastMessage.mes;
6417 lastMessage['swipes'].length++;6417 lastMessage.swipes.length++;
6418 if (lastMessage['swipe_id'] === lastMessage['swipes'].length - 1) {6418 if (lastMessage.swipe_id === lastMessage.swipes.length - 1) {
6419 lastMessage['title'] = title;6419 lastMessage.title = title;
6420 lastMessage['mes'] = getMessage;6420 lastMessage.mes = getMessage;
6421 lastMessage['gen_started'] = generation_started;6421 lastMessage.gen_started = generation_started;
6422 lastMessage['gen_finished'] = generationFinished;6422 lastMessage.gen_finished = generationFinished;
6423 lastMessage['send_date'] = getMessageTimeStamp();6423 lastMessage.send_date = getMessageTimeStamp();
6424 lastMessage['extra']['api'] = getGeneratingApi();6424 lastMessage.extra.api = getGeneratingApi();
6425 lastMessage['extra']['model'] = getGeneratingModel();6425 lastMessage.extra.model = getGeneratingModel();
6426 lastMessage['extra']['reasoning'] = reasoning;6426 lastMessage.extra.reasoning = reasoning;
6427 lastMessage['extra']['reasoning_duration'] = null;6427 lastMessage.extra.reasoning_duration = null;
6428 lastMessage['extra']['reasoning_signature'] = reasoningSignature;6428 lastMessage.extra.reasoning_signature = reasoningSignature;
6429 await processImageAttachment(lastMessage, { imageUrls });6429 await processImageAttachment(lastMessage, { imageUrls });
6430 if (power_user.message_token_count_enabled) {6430 if (power_user.message_token_count_enabled) {
6431 const tokenCountText = (reasoning || '') + lastMessage['mes'];6431 const tokenCountText = (reasoning || '') + lastMessage.mes;
6432 lastMessage['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);6432 lastMessage.extra.token_count = await getTokenCountAsync(tokenCountText, 0);
6433 }6433 }
6434 const chat_id = (chat.length - 1);6434 const chat_id = (chat.length - 1);
6435 !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type);6435 !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type);
6436 addOneMessage(chat[chat_id], { type: 'swipe' });6436 addOneMessage(chat[chat_id], { type: 'swipe' });
6437 !fromStreaming && await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, chat_id, type);6437 !fromStreaming && await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, chat_id, type);
6438 } else {6438 } else {
6439 lastMessage['mes'] = getMessage;6439 lastMessage.mes = getMessage;
6440 }6440 }
6441 } else if (type === 'append' || type === 'continue') {6441 } else if (type === 'append' || type === 'continue') {
6442 console.debug('Trying to append.');6442 console.debug('Trying to append.');
6443 oldMessage = lastMessage['mes'];6443 oldMessage = lastMessage.mes;
6444 lastMessage['title'] = title;6444 lastMessage.title = title;
6445 lastMessage['mes'] += getMessage;6445 lastMessage.mes += getMessage;
6446 lastMessage['gen_started'] = generation_started;6446 lastMessage.gen_started = generation_started;
6447 lastMessage['gen_finished'] = generationFinished;6447 lastMessage.gen_finished = generationFinished;
6448 lastMessage['send_date'] = getMessageTimeStamp();6448 lastMessage.send_date = getMessageTimeStamp();
6449 lastMessage['extra']['api'] = getGeneratingApi();6449 lastMessage.extra.api = getGeneratingApi();
6450 lastMessage['extra']['model'] = getGeneratingModel();6450 lastMessage.extra.model = getGeneratingModel();
6451 lastMessage['extra']['reasoning'] = reasoning;6451 lastMessage.extra.reasoning = reasoning;
6452 lastMessage['extra']['reasoning_duration'] = null;6452 lastMessage.extra.reasoning_duration = null;
6453 lastMessage['extra']['reasoning_signature'] = reasoningSignature;6453 lastMessage.extra.reasoning_signature = reasoningSignature;
6454 await processImageAttachment(lastMessage, { imageUrls });6454 await processImageAttachment(lastMessage, { imageUrls });
6455 if (power_user.message_token_count_enabled) {6455 if (power_user.message_token_count_enabled) {
6456 const tokenCountText = (reasoning || '') + lastMessage['mes'];6456 const tokenCountText = (reasoning || '') + lastMessage.mes;
6457 lastMessage['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);6457 lastMessage.extra.token_count = await getTokenCountAsync(tokenCountText, 0);
6458 }6458 }
6459 const chat_id = (chat.length - 1);6459 const chat_id = (chat.length - 1);
6460 !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type);6460 !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type);
6461 addOneMessage(chat[chat_id], { type: 'swipe' });6461 addOneMessage(chat[chat_id], { type: 'swipe' });
6462 !fromStreaming && await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, chat_id, type);6462 !fromStreaming && await eventSource.emit(event_types.CHARACTER_MESSAGE_RENDERED, chat_id, type);
6463 } else if (type === 'appendFinal') {6463 } else if (type === 'appendFinal') {
6464 oldMessage = lastMessage['mes'];6464 oldMessage = lastMessage.mes;
6465 console.debug('Trying to appendFinal.');6465 console.debug('Trying to appendFinal.');
6466 lastMessage['title'] = title;6466 lastMessage.title = title;
6467 lastMessage['mes'] = getMessage;6467 lastMessage.mes = getMessage;
6468 lastMessage['gen_started'] = generation_started;6468 lastMessage.gen_started = generation_started;
6469 lastMessage['gen_finished'] = generationFinished;6469 lastMessage.gen_finished = generationFinished;
6470 lastMessage['send_date'] = getMessageTimeStamp();6470 lastMessage.send_date = getMessageTimeStamp();
6471 lastMessage['extra']['api'] = getGeneratingApi();6471 lastMessage.extra.api = getGeneratingApi();
6472 lastMessage['extra']['model'] = getGeneratingModel();6472 lastMessage.extra.model = getGeneratingModel();
6473 lastMessage['extra']['reasoning'] += reasoning;6473 lastMessage.extra.reasoning += reasoning;
6474 lastMessage['extra']['reasoning_signature'] = reasoningSignature;6474 lastMessage.extra.reasoning_signature = reasoningSignature;
6475 await processImageAttachment(lastMessage, { imageUrls });6475 await processImageAttachment(lastMessage, { imageUrls });
6476 // We don't know if the reasoning duration extended, so we don't update it here on purpose.6476 // We don't know if the reasoning duration extended, so we don't update it here on purpose.
6477 if (power_user.message_token_count_enabled) {6477 if (power_user.message_token_count_enabled) {
6478 const tokenCountText = (reasoning || '') + lastMessage['mes'];6478 const tokenCountText = (reasoning || '') + lastMessage.mes;
6479 lastMessage['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);6479 lastMessage.extra.token_count = await getTokenCountAsync(tokenCountText, 0);
6480 }6480 }
6481 const chat_id = (chat.length - 1);6481 const chat_id = (chat.length - 1);
6482 !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type);6482 !fromStreaming && await eventSource.emit(event_types.MESSAGE_RECEIVED, chat_id, type);
@@ -6487,26 +6487,26 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title
6487 console.debug('entering chat update routine for non-swipe post');6487 console.debug('entering chat update routine for non-swipe post');
6488 const newMessage = {};6488 const newMessage = {};
6489 chat.push(newMessage);6489 chat.push(newMessage);
6490 newMessage['extra'] = {};6490 newMessage.extra = {};
6491 newMessage['name'] = name2;6491 newMessage.name = name2;
6492 newMessage['is_user'] = false;6492 newMessage.is_user = false;
6493 newMessage['send_date'] = getMessageTimeStamp();6493 newMessage.send_date = getMessageTimeStamp();
6494 newMessage['extra']['api'] = getGeneratingApi();6494 newMessage.extra.api = getGeneratingApi();
6495 newMessage['extra']['model'] = getGeneratingModel();6495 newMessage.extra.model = getGeneratingModel();
6496 newMessage['extra']['reasoning'] = reasoning;6496 newMessage.extra.reasoning = reasoning;
6497 newMessage['extra']['reasoning_duration'] = null;6497 newMessage.extra.reasoning_duration = null;
6498 newMessage['extra']['reasoning_signature'] = reasoningSignature;6498 newMessage.extra.reasoning_signature = reasoningSignature;
6499 if (power_user.trim_spaces) {6499 if (power_user.trim_spaces) {
6500 getMessage = getMessage.trim();6500 getMessage = getMessage.trim();
6501 }6501 }
6502 newMessage['mes'] = getMessage;6502 newMessage.mes = getMessage;
6503 newMessage['title'] = title;6503 newMessage.title = title;
6504 newMessage['gen_started'] = generation_started;6504 newMessage.gen_started = generation_started;
6505 newMessage['gen_finished'] = generationFinished;6505 newMessage.gen_finished = generationFinished;
65066506
6507 if (power_user.message_token_count_enabled) {6507 if (power_user.message_token_count_enabled) {
6508 const tokenCountText = (reasoning || '') + newMessage['mes'];6508 const tokenCountText = (reasoning || '') + newMessage.mes;
6509 newMessage['extra']['token_count'] = await getTokenCountAsync(tokenCountText, 0);6509 newMessage.extra.token_count = await getTokenCountAsync(tokenCountText, 0);
6510 }6510 }
65116511
6512 if (selected_group) {6512 if (selected_group) {
@@ -6515,9 +6515,9 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title
6515 if (characters[this_chid].avatar != 'none') {6515 if (characters[this_chid].avatar != 'none') {
6516 avatarImg = getThumbnailUrl('avatar', characters[this_chid].avatar);6516 avatarImg = getThumbnailUrl('avatar', characters[this_chid].avatar);
6517 }6517 }
6518 newMessage['force_avatar'] = avatarImg;6518 newMessage.force_avatar = avatarImg;
6519 newMessage['original_avatar'] = characters[this_chid].avatar;6519 newMessage.original_avatar = characters[this_chid].avatar;
6520 newMessage['extra']['gen_id'] = group_generation_id;6520 newMessage.extra.gen_id = group_generation_id;
6521 }6521 }
65226522
6523 await processImageAttachment(newMessage, { imageUrls });6523 await processImageAttachment(newMessage, { imageUrls });
@@ -6529,27 +6529,27 @@ export async function saveReply({ type, getMessage, fromStreaming = false, title
6529 }6529 }
65306530
6531 const item = chat[chat.length - 1];6531 const item = chat[chat.length - 1];
6532 if (item['swipe_info'] === undefined) {6532 if (item.swipe_info === undefined) {
6533 item['swipe_info'] = [];6533 item.swipe_info = [];
6534 }6534 }
6535 if (item['swipe_id'] !== undefined) {6535 if (item.swipe_id !== undefined) {
6536 const swipeId = item['swipe_id'];6536 const swipeId = item.swipe_id;
6537 item['swipes'][swipeId] = item['mes'];6537 item.swipes[swipeId] = item.mes;
6538 item['swipe_info'][swipeId] = {6538 item.swipe_info[swipeId] = {
6539 send_date: item['send_date'],6539 send_date: item.send_date,
6540 gen_started: item['gen_started'],6540 gen_started: item.gen_started,
6541 gen_finished: item['gen_finished'],6541 gen_finished: item.gen_finished,
6542 extra: structuredClone(item['extra']),6542 extra: structuredClone(item.extra),
6543 };6543 };
6544 } else {6544 } else {
6545 item['swipe_id'] = 0;6545 item.swipe_id = 0;
6546 item['swipes'] = [];6546 item.swipes = [];
6547 item['swipes'][0] = item['mes'];6547 item.swipes[0] = item.mes;
6548 item['swipe_info'][0] = {6548 item.swipe_info[0] = {
6549 send_date: item['send_date'],6549 send_date: item.send_date,
6550 gen_started: item['gen_started'],6550 gen_started: item.gen_started,
6551 gen_finished: item['gen_finished'],6551 gen_finished: item.gen_finished,
6552 extra: structuredClone(item['extra']),6552 extra: structuredClone(item.extra),
6553 };6553 };
6554 }6554 }
65556555
@@ -7155,7 +7155,7 @@ export async function saveChat({ chatName, withMetadata, mesId, force = false }
7155 return;7155 return;
7156 }7156 }
71577157
7158 characters[this_chid]['date_last_chat'] = Date.now();7158 characters[this_chid].date_last_chat = Date.now();
71597159
7160 const trimmedChat = (mesId !== undefined && mesId >= 0 && mesId < chat.length)7160 const trimmedChat = (mesId !== undefined && mesId >= 0 && mesId < chat.length)
7161 ? chat.slice(0, Number(mesId) + 1)7161 ? chat.slice(0, Number(mesId) + 1)
@@ -7387,15 +7387,19 @@ export async function getChat() {
7387 dataType: 'json',7387 dataType: 'json',
7388 contentType: 'application/json',7388 contentType: 'application/json',
7389 });7389 });
7390 if (response[0] !== undefined) {7390 if (Array.isArray(response) && response.length > 0) {
7391 /** @type {ChatHeader} */
7392 const chatHeader = response.shift();
7393 chat_metadata = chatHeader?.chat_metadata ?? {};
7391 chat.splice(0, chat.length, ...response);7394 chat.splice(0, chat.length, ...response);
7392 chat_metadata = chat[0]['chat_metadata'] ?? {};
7393
7394 chat.shift();
7395 chat.forEach(ensureMessageMediaIsArray);7395 chat.forEach(ensureMessageMediaIsArray);
7396 } else {
7397 // An empty/corrupted chat file
7398 chat.splice(0, chat.length);
7399 chat_metadata = {};
7396 }7400 }
7397 if (!chat_metadata['integrity']) {7401 if (!chat_metadata.integrity) {
7398 chat_metadata['integrity'] = uuidv4();7402 chat_metadata.integrity = uuidv4();
7399 }7403 }
7400 await getChatResult();7404 await getChatResult();
7401 eventSource.emit('chatLoaded', { detail: { id: this_chid, character: characters[this_chid] } });7405 eventSource.emit('chatLoaded', { detail: { id: this_chid, character: characters[this_chid] } });
@@ -7460,9 +7464,9 @@ function getFirstMessage() {
7460 message.mes = swipes[0];7464 message.mes = swipes[0];
7461 }7465 }
74627466
7463 message['swipe_id'] = 0;7467 message.swipe_id = 0;
7464 message['swipes'] = swipes;7468 message.swipes = swipes;
7465 message['swipe_info'] = swipes.map(_ => ({7469 message.swipe_info = swipes.map(_ => ({
7466 send_date: message.send_date,7470 send_date: message.send_date,
7467 gen_started: void 0,7471 gen_started: void 0,
7468 gen_finished: void 0,7472 gen_finished: void 0,
@@ -7476,7 +7480,7 @@ function getFirstMessage() {
7476export async function openCharacterChat(file_name) {7480export async function openCharacterChat(file_name) {
7477 await waitUntilCondition(() => !isChatSaving, debounce_timeout.extended, 10);7481 await waitUntilCondition(() => !isChatSaving, debounce_timeout.extended, 10);
7478 await clearChat();7482 await clearChat();
7479 characters[this_chid]['chat'] = file_name;7483 characters[this_chid].chat = file_name;
7480 chat.length = 0;7484 chat.length = 0;
7481 chat_metadata = {};7485 chat_metadata = {};
7482 await getChat();7486 await getChat();
@@ -7862,7 +7866,7 @@ function updateMessage(div) {
7862 const mes = chat[mesElement.attr('mesid')];7866 const mes = chat[mesElement.attr('mesid')];
78637867
7864 // editing old messages7868 // editing old messages
7865 mes['extra'] ??= {};7869 mes.extra ??= {};
78667870
7867 let regexPlacement;7871 let regexPlacement;
7868 if (mes?.is_user) {7872 if (mes?.is_user) {
@@ -7893,10 +7897,10 @@ function updateMessage(div) {
7893 if (bias) {7897 if (bias) {
7894 text = removeMacros(text);7898 text = removeMacros(text);
7895 }7899 }
7896 mes['mes'] = text;7900 mes.mes = text;
7897 if (mes['swipe_id'] !== undefined) {7901 if (mes.swipe_id !== undefined) {
7898 ensureSwipes(mes);7902 ensureSwipes(mes);
7899 mes['swipes'][mes['swipe_id']] = text;7903 mes.swipes[mes.swipe_id] = text;
7900 }7904 }
79017905
7902 if (mes?.is_system || mes?.is_user || mes.extra?.type === system_message_types.NARRATOR) {7906 if (mes?.is_system || mes?.is_user || mes.extra?.type === system_message_types.NARRATOR) {
@@ -7905,7 +7909,7 @@ function updateMessage(div) {
7905 mes.extra.bias = null;7909 mes.extra.bias = null;
7906 }7910 }
79077911
7908 chat_metadata['tainted'] = true;7912 chat_metadata.tainted = true;
79097913
7910 return { mesBlock, text, mes, bias };7914 return { mesBlock, text, mes, bias };
7911}7915}
@@ -8019,7 +8023,7 @@ export async function messageEdit(editMessageId) {
8019 * @param {number} [messageId=this_edit_mes_id]8023 * @param {number} [messageId=this_edit_mes_id]
8020 */8024 */
8021async function messageEditCancel(messageId = this_edit_mes_id) {8025async function messageEditCancel(messageId = this_edit_mes_id) {
8022 let text = chat[messageId]['mes'];8026 let text = chat[messageId].mes;
8023 let thisMesDiv;8027 let thisMesDiv;
8024 // If this is the button then select it's parent. Otherwise, select by messageId.8028 // If this is the button then select it's parent. Otherwise, select by messageId.
8025 if (this?.classList?.contains('mes_edit_cancel')) {8029 if (this?.classList?.contains('mes_edit_cancel')) {
@@ -8167,7 +8171,7 @@ async function messageEditDone(div) {
8167export async function getChatsFromFiles(data, isGroupChat) {8171export async function getChatsFromFiles(data, isGroupChat) {
8168 const context = getContext();8172 const context = getContext();
8169 let chat_dict = {};8173 let chat_dict = {};
8170 let chat_list = Object.values(data).sort((a, b) => a['file_name'].localeCompare(b['file_name'])).reverse();8174 let chat_list = Object.values(data).sort((a, b) => a.file_name.localeCompare(b.file_name)).reverse();
81718175
8172 let chat_promise = chat_list.map(({ file_name }) => {8176 let chat_promise = chat_list.map(({ file_name }) => {
8173 return new Promise(async (res, rej) => {8177 return new Promise(async (res, rej) => {
@@ -8244,7 +8248,7 @@ export async function getPastCharacterChats(characterId = null) {
8244 }8248 }
82458249
8246 const chats = Object.values(data);8250 const chats = Object.values(data);
8247 return chats.sort((a, b) => a['file_name'].localeCompare(b['file_name'])).reverse();8251 return chats.sort((a, b) => a.file_name.localeCompare(b.file_name)).reverse();
8248}8252}
82498253
8250/**8254/**
@@ -8256,9 +8260,9 @@ export function getCurrentChatDetails() {
8256 }8260 }
82578261
8258 const group = selected_group ? groups.find(x => x.id === selected_group) : null;8262 const group = selected_group ? groups.find(x => x.id === selected_group) : null;
8259 const currentChat = selected_group ? group?.chat_id : characters[this_chid]['chat'];8263 const currentChat = selected_group ? group?.chat_id : characters[this_chid].chat;
8260 const displayName = selected_group ? group?.name : characters[this_chid].name;8264 const displayName = selected_group ? group?.name : characters[this_chid].name;
8261 const avatarImg = selected_group ? group?.avatar_url : getThumbnailUrl('avatar', characters[this_chid]['avatar']);8265 const avatarImg = selected_group ? group?.avatar_url : getThumbnailUrl('avatar', characters[this_chid].avatar);
8262 return { sessionName: currentChat, group: group, characterName: displayName, avatarImgURL: avatarImg };8266 return { sessionName: currentChat, group: group, characterName: displayName, avatarImgURL: avatarImg };
8263}8267}
82648268
@@ -8722,9 +8726,9 @@ export async function setCharacterSettingsOverrides() {
8722 return;8726 return;
8723 }8727 }
87248728
8725 const scenarioOverrideValue = chat_metadata['scenario'] || '';8729 const scenarioOverrideValue = chat_metadata.scenario || '';
8726 const exampleMessagesValue = chat_metadata['mes_example'] || '';8730 const exampleMessagesValue = chat_metadata.mes_example || '';
8727 const systemPromptValue = chat_metadata['system_prompt'] || '';8731 const systemPromptValue = chat_metadata.system_prompt || '';
8728 const isGroup = !!selected_group;8732 const isGroup = !!selected_group;
87298733
8730 const $template = $(await renderTemplateAsync('scenarioOverride'));8734 const $template = $(await renderTemplateAsync('scenarioOverride'));
@@ -8771,9 +8775,9 @@ export async function setCharacterSettingsOverrides() {
8771 allowVerticalScrolling: true,8775 allowVerticalScrolling: true,
8772 });8776 });
87738777
8774 chat_metadata['scenario'] = pendingChanges.scenario;8778 chat_metadata.scenario = pendingChanges.scenario;
8775 chat_metadata['mes_example'] = pendingChanges.examples;8779 chat_metadata.mes_example = pendingChanges.examples;
8776 chat_metadata['system_prompt'] = pendingChanges.system_prompt;8780 chat_metadata.system_prompt = pendingChanges.system_prompt;
8777 await saveMetadata();8781 await saveMetadata();
8778}8782}
87798783
@@ -9071,7 +9075,7 @@ export async function deleteSwipe(swipeId = null, messageId = chat.length - 1) {
9071 // Select the next swipe, or the one before if it was the last one9075 // Select the next swipe, or the one before if it was the last one
9072 const newSwipeId = Math.min(swipeId, message.swipes.length - 1);9076 const newSwipeId = Math.min(swipeId, message.swipes.length - 1);
90739077
9074 chat_metadata['tainted'] = true;9078 chat_metadata.tainted = true;
90759079
9076 messageId = Number(messageId);9080 messageId = Number(messageId);
9077 swipeId = Number(swipeId);9081 swipeId = Number(swipeId);
@@ -9585,7 +9589,7 @@ export async function createOrEditCharacter(e) {
9585 !isNewChat &&9589 !isNewChat &&
9586 message.mes &&9590 message.mes &&
9587 !selected_group &&9591 !selected_group &&
9588 !chat_metadata['tainted'] &&9592 !chat_metadata.tainted &&
9589 (chat.length === 0 || (chat.length === 1 && !chat[0].is_user && !chat[0].is_system));9593 (chat.length === 0 || (chat.length === 1 && !chat[0].is_user && !chat[0].is_system));
95909594
9591 if (shouldRegenerateMessage) {9595 if (shouldRegenerateMessage) {
@@ -9745,7 +9749,7 @@ export async function swipe(event, direction, { source, repeated, message = chat
9745 }9749 }
97469750
9747 //Clamp Id between swipes.9751 //Clamp Id between swipes.
9748 let clampedId = clamp(chat[mesId]['swipe_id'], 0, Math.max(0, chat[mesId]['swipes'].length - 1));9752 let clampedId = clamp(chat[mesId].swipe_id, 0, Math.max(0, chat[mesId].swipes.length - 1));
97499753
9750 await updateSwipeCounter(mesId);9754 await updateSwipeCounter(mesId);
9751 //Fallback.9755 //Fallback.
@@ -9837,7 +9841,7 @@ export async function swipe(event, direction, { source, repeated, message = chat
9837 */9841 */
9838 async function loadFromSwipeId(mesId, newSwipeId) {9842 async function loadFromSwipeId(mesId, newSwipeId) {
9839 //Update the swipe_id.9843 //Update the swipe_id.
9840 chat[mesId]['swipe_id'] = newSwipeId;9844 chat[mesId].swipe_id = newSwipeId;
98419845
9842 clearMessageData(chat[mesId]);9846 clearMessageData(chat[mesId]);
98439847
@@ -9909,7 +9913,8 @@ export async function swipe(event, direction, { source, repeated, message = chat
9909 return true;9913 return true;
9910 };9914 };
9911 //Wait for the animation's end. https://developer.mozilla.org/en-US/docs/Web/API/Animation/finished9915 //Wait for the animation's end. https://developer.mozilla.org/en-US/docs/Web/API/Animation/finished
9912 const animation = swipedElementsDiv[0]?.getAnimations().filter((a) => a['animationName'] == 'slide')[0];9916 const animations = swipedElementsDiv[0]?.getAnimations() ?? [];
9917 const animation = animations.filter((a) => a instanceof globalThis.CSSAnimation && a.animationName == 'slide')[0];
9913 try {9918 try {
9914 await Promise.race([animation?.finished, createTimeout(duration * 2, `The ${duration}ms swipe animation has not ended after ${duration * 2}ms. It has been skipped.`)].filter(Boolean));9919 await Promise.race([animation?.finished, createTimeout(duration * 2, `The ${duration}ms swipe animation has not ended after ${duration * 2}ms. It has been skipped.`)].filter(Boolean));
9915 } catch (error) {9920 } catch (error) {
@@ -9997,7 +10002,7 @@ export async function swipe(event, direction, { source, repeated, message = chat
999710002
9998 const tokenCountText = (chat[mesId]?.extra?.reasoning || '') + chat[mesId].mes;10003 const tokenCountText = (chat[mesId]?.extra?.reasoning || '') + chat[mesId].mes;
9999 const tokenCount = await getTokenCountAsync(tokenCountText, 0);10004 const tokenCount = await getTokenCountAsync(tokenCountText, 0);
10000 chat[mesId]['extra']['token_count'] = tokenCount;10005 chat[mesId].extra.token_count = tokenCount;
10001 thisMesDiv.find('.tokenCounterDisplay').text(`${tokenCount}t`);10006 thisMesDiv.find('.tokenCounterDisplay').text(`${tokenCount}t`);
10002 }10007 }
10003 }10008 }
@@ -10036,20 +10041,20 @@ export async function swipe(event, direction, { source, repeated, message = chat
10036 // Make sure ad-hoc changes to extras are saved before swiping away10041 // Make sure ad-hoc changes to extras are saved before swiping away
10037 syncMesToSwipe(mesId);10042 syncMesToSwipe(mesId);
1003810043
10039 if (chat[mesId]['swipe_id'] === undefined) { // if there is no swipe-message in the last spot of the chat array10044 if (chat[mesId].swipe_id === undefined) { // if there is no swipe-message in the last spot of the chat array
10040 chat[mesId]['swipe_id'] = 0; // set it to id 010045 chat[mesId].swipe_id = 0; // set it to id 0
10041 chat[mesId]['swipes'] = []; // empty the array10046 chat[mesId].swipes = []; // empty the array
10042 chat[mesId]['swipe_info'] = [];10047 chat[mesId].swipe_info = [];
10043 chat[mesId]['swipes'][0] = chat[mesId]['mes']; //assign swipe array with last chat[mesId] from chat10048 chat[mesId].swipes[0] = chat[mesId].mes; //assign swipe array with last chat[mesId] from chat
10044 chat[mesId]['swipe_info'][0] = {10049 chat[mesId].swipe_info[0] = {
10045 'send_date': chat[mesId]['send_date'],10050 'send_date': chat[mesId].send_date,
10046 'gen_started': chat[mesId]['gen_started'],10051 'gen_started': chat[mesId].gen_started,
10047 'gen_finished': chat[mesId]['gen_finished'],10052 'gen_finished': chat[mesId].gen_finished,
10048 'extra': structuredClone(chat[mesId]['extra']),10053 'extra': structuredClone(chat[mesId].extra),
10049 };10054 };
10050 }10055 }
10051 // If the user is holding down the key and we're at the last or first swipe, don't do anything.10056 // If the user is holding down the key and we're at the last or first swipe, don't do anything.
10052 let isLastSwipe = (direction === SWIPE_DIRECTION.RIGHT) ? (chat[mesId].swipe_id === Math.max(0, chat[mesId]['swipes'].length - 1)) : chat[mesId].swipe_id === 0;10057 let isLastSwipe = (direction === SWIPE_DIRECTION.RIGHT) ? (chat[mesId].swipe_id === Math.max(0, chat[mesId].swipes.length - 1)) : chat[mesId].swipe_id === 0;
10053 if (source === SWIPE_SOURCE.KEYBOARD && repeated && isLastSwipe) {10058 if (source === SWIPE_SOURCE.KEYBOARD && repeated && isLastSwipe) {
10054 await endSwipe();10059 await endSwipe();
10055 return;10060 return;
@@ -10065,12 +10070,12 @@ export async function swipe(event, direction, { source, repeated, message = chat
10065 if (forceSwipeId == null) newSwipeId--;10070 if (forceSwipeId == null) newSwipeId--;
10066 //Loop to last swipe if negative.10071 //Loop to last swipe if negative.
10067 if (newSwipeId < 0) {10072 if (newSwipeId < 0) {
10068 newSwipeId = Math.max(0, chat[mesId]['swipes'].length - 1);10073 newSwipeId = Math.max(0, chat[mesId].swipes.length - 1);
10069 }10074 }
10070 //Limit swipe_id to swipes.10075 //Limit swipe_id to swipes.
10071 if (newSwipeId > chat[mesId]['swipes'].length - 1) {10076 if (newSwipeId > chat[mesId].swipes.length - 1) {
10072 toastr.warning(`The swipe_id for message #${mesId} was ${newSwipeId}. It has been reset to ${chat[mesId]['swipes'].length - 1}.`);10077 toastr.warning(`The swipe_id for message #${mesId} was ${newSwipeId}. It has been reset to ${chat[mesId].swipes.length - 1}.`);
10073 chat[mesId]['swipe_id'] = chat[mesId]['swipes'].length - 1;10078 chat[mesId].swipe_id = chat[mesId].swipes.length - 1;
10074 await endSwipe();10079 await endSwipe();
10075 return;10080 return;
10076 }10081 }
@@ -10085,24 +10090,24 @@ export async function swipe(event, direction, { source, repeated, message = chat
10085 //Minimum of zero.10090 //Minimum of zero.
10086 if (newSwipeId < 0) {10091 if (newSwipeId < 0) {
10087 toastr.warning(`The swipe_id for message #${mesId} was ${newSwipeId}. It has been reset to zero.`);10092 toastr.warning(`The swipe_id for message #${mesId} was ${newSwipeId}. It has been reset to zero.`);
10088 chat[mesId]['swipe_id'] = 0;10093 chat[mesId].swipe_id = 0;
10089 await endSwipe();10094 await endSwipe();
10090 return;10095 return;
10091 }10096 }
1009210097
10093 //If overswiping.10098 //If overswiping.
10094 if (newSwipeId >= chat[mesId]['swipes'].length) {10099 if (newSwipeId >= chat[mesId].swipes.length) {
10095 newSwipeId = chat[mesId]['swipes'].length;10100 newSwipeId = chat[mesId].swipes.length;
1009610101
10097 //Update the swipe_id.10102 //Update the swipe_id.
10098 chat[mesId]['swipe_id'] = newSwipeId;10103 chat[mesId].swipe_id = newSwipeId;
1009910104
10100 const overswipe = getOverswipeBehavior(mesId);10105 const overswipe = getOverswipeBehavior(mesId);
1010110106
10102 //Cancel the generation.10107 //Cancel the generation.
10103 if (overswipe == OVERSWIPE_BEHAVIOR.NONE) {10108 if (overswipe == OVERSWIPE_BEHAVIOR.NONE) {
10104 //Cancel swipe.10109 //Cancel swipe.
10105 chat[mesId]['swipe_id'] = originalSwipeId;10110 chat[mesId].swipe_id = originalSwipeId;
10106 await endSwipe();10111 await endSwipe();
10107 return;10112 return;
10108 }10113 }
@@ -11430,7 +11435,7 @@ jQuery(async function () {
11430 chatElement.find(`.mes[mesid="${this_del_mes}"]`).nextAll('div').remove();11435 chatElement.find(`.mes[mesid="${this_del_mes}"]`).nextAll('div').remove();
11431 chatElement.find(`.mes[mesid="${this_del_mes}"]`).remove();11436 chatElement.find(`.mes[mesid="${this_del_mes}"]`).remove();
11432 chat.length = this_del_mes;11437 chat.length = this_del_mes;
11433 chat_metadata['tainted'] = true;11438 chat_metadata.tainted = true;
11434 await saveChatConditional();11439 await saveChatConditional();
11435 chatElement.scrollTop(chatElement[0].scrollHeight);11440 chatElement.scrollTop(chatElement[0].scrollHeight);
11436 await eventSource.emit(event_types.MESSAGE_DELETED, chat.length);11441 await eventSource.emit(event_types.MESSAGE_DELETED, chat.length);
@@ -11517,7 +11522,7 @@ jQuery(async function () {
11517 if (this_chid !== undefined || selected_group || name2 === neutralCharacterName) {11522 if (this_chid !== undefined || selected_group || name2 === neutralCharacterName) {
11518 try {11523 try {
11519 const messageId = $(this).closest('.mes').attr('mesid');11524 const messageId = $(this).closest('.mes').attr('mesid');
11520 const text = chat[messageId]['mes'];11525 const text = chat[messageId].mes;
11521 await copyText(text);11526 await copyText(text);
11522 toastr.info('Copied!', '', { timeOut: 2000 });11527 toastr.info('Copied!', '', { timeOut: 2000 });
11523 } catch (err) {11528 } catch (err) {
@@ -11544,8 +11549,8 @@ jQuery(async function () {
11544 let mes_edited = chatElement.find(`[mesid="${this_edit_mes_id}"]`).find('.mes_edit_done');11549 let mes_edited = chatElement.find(`[mesid="${this_edit_mes_id}"]`).find('.mes_edit_done');
11545 if (Number(edit_mes_id) == chat.length - 1) { //if the generating swipe (...)11550 if (Number(edit_mes_id) == chat.length - 1) { //if the generating swipe (...)
11546 let run_edit = true;11551 let run_edit = true;
11547 if (chat[edit_mes_id]['swipe_id'] !== undefined) {11552 if (chat[edit_mes_id].swipe_id !== undefined) {
11548 if (chat[edit_mes_id]['swipes'].length === chat[edit_mes_id]['swipe_id']) {11553 if (chat[edit_mes_id].swipes.length === chat[edit_mes_id].swipe_id) {
11549 run_edit = false;11554 run_edit = false;
11550 }11555 }
11551 }11556 }
@@ -11684,8 +11689,8 @@ jQuery(async function () {
11684 $(document).on('click', '.mes_edit_delete', async function (event, customData) {11689 $(document).on('click', '.mes_edit_delete', async function (event, customData) {
11685 const fromSlashCommand = customData?.fromSlashCommand || false;11690 const fromSlashCommand = customData?.fromSlashCommand || false;
11686 const message = chat[this_edit_mes_id];11691 const message = chat[this_edit_mes_id];
11687 const selectedSwipe = message['swipe_id'] ?? undefined;11692 const selectedSwipe = message.swipe_id ?? undefined;
11688 const swipesArray = Array.isArray(message['swipes']) ? message['swipes'] : [];11693 const swipesArray = Array.isArray(message.swipes) ? message.swipes : [];
11689 const canDeleteSwipe = power_user.confirm_message_delete && !fromSlashCommand && !message.is_user && swipesArray.length > 1 && this_edit_mes_id === chat.length - 1 && selectedSwipe !== undefined;11694 const canDeleteSwipe = power_user.confirm_message_delete && !fromSlashCommand && !message.is_user && swipesArray.length > 1 && this_edit_mes_id === chat.length - 1 && selectedSwipe !== undefined;
11690 await deleteMessage(Number(this_edit_mes_id), canDeleteSwipe ? selectedSwipe : undefined, power_user.confirm_message_delete && fromSlashCommand !== true);11695 await deleteMessage(Number(this_edit_mes_id), canDeleteSwipe ? selectedSwipe : undefined, power_user.confirm_message_delete && fromSlashCommand !== true);
11691 });11696 });
@@ -12102,7 +12107,7 @@ jQuery(async function () {
12102 });12107 });
1210312108
12104 // Remember the chat currently selected, so we can reload it after the replacement12109 // Remember the chat currently selected, so we can reload it after the replacement
12105 const currentChatFile = characters[this_chid]['chat'];12110 const currentChatFile = characters[this_chid].chat;
12106 async function postReplace() {12111 async function postReplace() {
12107 await openCharacterChat(currentChatFile);12112 await openCharacterChat(currentChatFile);
12108 }12113 }
public/scripts/RossAscends-mods.js+4 -4
@@ -996,7 +996,7 @@ export function initRossMods() {
996 }996 }
997997
998 //Enter to send when send_textarea in focus998 //Enter to send when send_textarea in focus
999 if (document.activeElement == hotkeyTargets['send_textarea']) {999 if (document.activeElement == hotkeyTargets.send_textarea) {
1000 const sendOnEnter = shouldSendOnEnter();1000 const sendOnEnter = shouldSendOnEnter();
1001 if (!event.isComposing && !event.shiftKey && !event.ctrlKey && !event.altKey && event.key == 'Enter' && sendOnEnter) {1001 if (!event.isComposing && !event.shiftKey && !event.ctrlKey && !event.altKey && event.key == 'Enter' && sendOnEnter) {
1002 event.preventDefault();1002 event.preventDefault();
@@ -1004,7 +1004,7 @@ export function initRossMods() {
1004 return;1004 return;
1005 }1005 }
1006 }1006 }
1007 if (document.activeElement == hotkeyTargets['dialogue_popup_input'] && !isMobile()) {1007 if (document.activeElement == hotkeyTargets.dialogue_popup_input && !isMobile()) {
1008 if (!event.shiftKey && !event.ctrlKey && event.key == 'Enter') {1008 if (!event.shiftKey && !event.ctrlKey && event.key == 'Enter') {
1009 event.preventDefault();1009 event.preventDefault();
1010 $('#dialogue_popup_ok').trigger('click');1010 $('#dialogue_popup_ok').trigger('click');
@@ -1139,7 +1139,7 @@ export function initRossMods() {
11391139
1140 if (event.ctrlKey && event.key == 'ArrowUp') { //edits last USER message if chatbar is empty and focused1140 if (event.ctrlKey && event.key == 'ArrowUp') { //edits last USER message if chatbar is empty and focused
1141 if (1141 if (
1142 hotkeyTargets['send_textarea'].value === '' &&1142 hotkeyTargets.send_textarea.value === '' &&
1143 chatbarInFocus === true &&1143 chatbarInFocus === true &&
1144 ($('.swipe_right:last').css('display') === 'flex' || $('.last_mes').attr('is_system') === 'true') &&1144 ($('.swipe_right:last').css('display') === 'flex' || $('.last_mes').attr('is_system') === 'true') &&
1145 $('#character_popup').css('display') === 'none' &&1145 $('#character_popup').css('display') === 'none' &&
@@ -1158,7 +1158,7 @@ export function initRossMods() {
1158 if (event.key == 'ArrowUp') { //edits last message if chatbar is empty and focused1158 if (event.key == 'ArrowUp') { //edits last message if chatbar is empty and focused
1159 console.log('got uparrow input');1159 console.log('got uparrow input');
1160 if (1160 if (
1161 hotkeyTargets['send_textarea'].value === '' &&1161 hotkeyTargets.send_textarea.value === '' &&
1162 chatbarInFocus === true &&1162 chatbarInFocus === true &&
1163 //$('.swipe_right:last').css('display') === 'flex' &&1163 //$('.swipe_right:last').css('display') === 'flex' &&
1164 $('.last_mes .mes_buttons').is(':visible') &&1164 $('.last_mes .mes_buttons').is(':visible') &&
public/scripts/bookmarks.js+9 -9
@@ -103,8 +103,8 @@ async function getBookmarkName({ isReplace = false, forceName = null } = {}) {
103103
104function getMainChatName() {104function getMainChatName() {
105 if (chat_metadata) {105 if (chat_metadata) {
106 if (chat_metadata['main_chat']) {106 if (chat_metadata.main_chat) {
107 return chat_metadata['main_chat'];107 return chat_metadata.main_chat;
108 }108 }
109 // groups didn't support bookmarks before chat metadata was introduced109 // groups didn't support bookmarks before chat metadata was introduced
110 else if (selected_group) {110 else if (selected_group) {
@@ -112,8 +112,8 @@ function getMainChatName() {
112 }112 }
113 else if (characters[this_chid].chat && characters[this_chid].chat.includes(bookmarkNameToken)) {113 else if (characters[this_chid].chat && characters[this_chid].chat.includes(bookmarkNameToken)) {
114 const tokenIndex = characters[this_chid].chat.lastIndexOf(bookmarkNameToken);114 const tokenIndex = characters[this_chid].chat.lastIndexOf(bookmarkNameToken);
115 chat_metadata['main_chat'] = characters[this_chid].chat.substring(0, tokenIndex).trim();115 chat_metadata.main_chat = characters[this_chid].chat.substring(0, tokenIndex).trim();
116 return chat_metadata['main_chat'];116 return chat_metadata.main_chat;
117 }117 }
118 }118 }
119 return null;119 return null;
@@ -127,7 +127,7 @@ export function showBookmarksButtons() {
127 $('#option_convert_to_group').show();127 $('#option_convert_to_group').show();
128 }128 }
129129
130 if (chat_metadata['main_chat']) {130 if (chat_metadata.main_chat) {
131 // In bookmark chat131 // In bookmark chat
132 $('#option_back_to_main').show();132 $('#option_back_to_main').show();
133 $('#option_new_bookmark').show();133 $('#option_new_bookmark').show();
@@ -184,10 +184,10 @@ export async function createBranch(mesId) {
184 if (typeof lastMes.extra !== 'object') {184 if (typeof lastMes.extra !== 'object') {
185 lastMes.extra = {};185 lastMes.extra = {};
186 }186 }
187 if (typeof lastMes.extra['branches'] !== 'object') {187 if (typeof lastMes.extra.branches !== 'object') {
188 lastMes.extra['branches'] = [];188 lastMes.extra.branches = [];
189 }189 }
190 lastMes.extra['branches'].push(name);190 lastMes.extra.branches.push(name);
191 return name;191 return name;
192}192}
193193
@@ -236,7 +236,7 @@ export async function createNewBookmark(mesId, { forceName = null } = {}) {
236 await saveChat({ chatName: name, withMetadata: newMetadata, mesId });236 await saveChat({ chatName: name, withMetadata: newMetadata, mesId });
237 }237 }
238238
239 lastMes.extra['bookmark_link'] = name;239 lastMes.extra.bookmark_link = name;
240240
241 const mes = $(`.mes[mesid="${mesId}"]`);241 const mes = $(`.mes[mesid="${mesId}"]`);
242 updateBookmarkDisplay(mes, name);242 updateBookmarkDisplay(mes, name);
public/scripts/cfg-scale.js+14 -14
@@ -42,13 +42,13 @@ function setCharCfg(tempValue, setting) {
4242
43 switch (setting) {43 switch (setting) {
44 case settingType.guidance_scale:44 case settingType.guidance_scale:
45 tempCharaCfg['guidance_scale'] = Number(tempValue);45 tempCharaCfg.guidance_scale = Number(tempValue);
46 break;46 break;
47 case settingType.negative_prompt:47 case settingType.negative_prompt:
48 tempCharaCfg['negative_prompt'] = tempValue;48 tempCharaCfg.negative_prompt = tempValue;
49 break;49 break;
50 case settingType.positive_prompt:50 case settingType.positive_prompt:
51 tempCharaCfg['positive_prompt'] = tempValue;51 tempCharaCfg.positive_prompt = tempValue;
52 break;52 break;
53 default:53 default:
54 return false;54 return false;
@@ -239,31 +239,31 @@ function migrateSettings() {
239239
240 if (power_user.guidance_scale) {240 if (power_user.guidance_scale) {
241 extension_settings.cfg.global.guidance_scale = power_user.guidance_scale;241 extension_settings.cfg.global.guidance_scale = power_user.guidance_scale;
242 delete power_user['guidance_scale'];242 delete power_user.guidance_scale;
243 performSettingsSave = true;243 performSettingsSave = true;
244 }244 }
245245
246 if (power_user.negative_prompt) {246 if (power_user.negative_prompt) {
247 extension_settings.cfg.global.negative_prompt = power_user.negative_prompt;247 extension_settings.cfg.global.negative_prompt = power_user.negative_prompt;
248 delete power_user['negative_prompt'];248 delete power_user.negative_prompt;
249 performSettingsSave = true;249 performSettingsSave = true;
250 }250 }
251251
252 if (chat_metadata['cfg_negative_combine']) {252 if (chat_metadata.cfg_negative_combine) {
253 chat_metadata[metadataKeys.prompt_combine] = chat_metadata['cfg_negative_combine'];253 chat_metadata[metadataKeys.prompt_combine] = chat_metadata.cfg_negative_combine;
254 chat_metadata['cfg_negative_combine'] = undefined;254 chat_metadata.cfg_negative_combine = undefined;
255 performMetaSave = true;255 performMetaSave = true;
256 }256 }
257257
258 if (chat_metadata['cfg_negative_insertion_depth']) {258 if (chat_metadata.cfg_negative_insertion_depth) {
259 chat_metadata[metadataKeys.prompt_insertion_depth] = chat_metadata['cfg_negative_insertion_depth'];259 chat_metadata[metadataKeys.prompt_insertion_depth] = chat_metadata.cfg_negative_insertion_depth;
260 chat_metadata['cfg_negative_insertion_depth'] = undefined;260 chat_metadata.cfg_negative_insertion_depth = undefined;
261 performMetaSave = true;261 performMetaSave = true;
262 }262 }
263263
264 if (chat_metadata['cfg_negative_separator']) {264 if (chat_metadata.cfg_negative_separator) {
265 chat_metadata[metadataKeys.prompt_separator] = chat_metadata['cfg_negative_separator'];265 chat_metadata[metadataKeys.prompt_separator] = chat_metadata.cfg_negative_separator;
266 chat_metadata['cfg_negative_separator'] = undefined;266 chat_metadata.cfg_negative_separator = undefined;
267 performMetaSave = true;267 performMetaSave = true;
268 }268 }
269269
public/scripts/chat-templates.js+6 -6
@@ -148,8 +148,8 @@ export async function bindModelTemplates(power_user, online_status) {
148 ?? power_user.model_templates_mappings[chatTemplateHash]148 ?? power_user.model_templates_mappings[chatTemplateHash]
149 ?? {};149 ?? {};
150 const bindingsMatch = bindModelTemplates150 const bindingsMatch = bindModelTemplates
151 && power_user.context.preset == bindModelTemplates['context']151 && power_user.context.preset == bindModelTemplates.context
152 && (!power_user.instruct.enabled || power_user.instruct.preset === bindModelTemplates['instruct']);152 && (!power_user.instruct.enabled || power_user.instruct.preset === bindModelTemplates.instruct);
153153
154 const bound = [];154 const bound = [];
155155
@@ -160,21 +160,21 @@ export async function bindModelTemplates(power_user, online_status) {
160 toastr.info(t`Context preset for ${online_status} will use defaults when loaded the next time.`);160 toastr.info(t`Context preset for ${online_status} will use defaults when loaded the next time.`);
161 } else {161 } else {
162 if (power_user.context_derived) {162 if (power_user.context_derived) {
163 if (power_user.context.preset !== bindModelTemplates['context']) {163 if (power_user.context.preset !== bindModelTemplates.context) {
164 bound.push(`${power_user.context.preset} context preset`);164 bound.push(`${power_user.context.preset} context preset`);
165 // toastr.info(`Bound ${power_user.context.preset} preset to currently loaded model and all models that share its chat template.`);165 // toastr.info(`Bound ${power_user.context.preset} preset to currently loaded model and all models that share its chat template.`);
166166
167 // map current preset to current chat template hash167 // map current preset to current chat template hash
168 bindModelTemplates['context'] = power_user.context.preset;168 bindModelTemplates.context = power_user.context.preset;
169 }169 }
170 } else {170 } else {
171 toastr.warning(t`Note: Context derivation is disabled. Not including context preset.`);171 toastr.warning(t`Note: Context derivation is disabled. Not including context preset.`);
172 }172 }
173 if (power_user.instruct.enabled) {173 if (power_user.instruct.enabled) {
174 if (power_user.instruct_derived) {174 if (power_user.instruct_derived) {
175 if (power_user.instruct.preset !== bindModelTemplates['instruct']) {175 if (power_user.instruct.preset !== bindModelTemplates.instruct) {
176 bound.push(`${power_user.instruct.preset} instruct preset`);176 bound.push(`${power_user.instruct.preset} instruct preset`);
177 bindModelTemplates['instruct'] = power_user.instruct.preset;177 bindModelTemplates.instruct = power_user.instruct.preset;
178 }178 }
179 } else {179 } else {
180 toastr.warning(t`Note: Instruct derivation is disabled. Not including instruct preset.`);180 toastr.warning(t`Note: Instruct derivation is disabled. Not including instruct preset.`);
public/scripts/chats.js+4 -4
@@ -685,7 +685,7 @@ export function formatCreatorNotes(text, avatarId) {
685 const preference = new StylesPreference(avatarId);685 const preference = new StylesPreference(avatarId);
686 const sanitizeStyles = !preference.get();686 const sanitizeStyles = !preference.get();
687 const decodeStyleParam = { prefix: sanitizeStyles ? '#creator_notes_spoiler ' : '' };687 const decodeStyleParam = { prefix: sanitizeStyles ? '#creator_notes_spoiler ' : '' };
688 /** @type {import('dompurify').Config & { MESSAGE_SANITIZE: boolean }} */688 /** @type {import('dompurify').Config} */
689 const config = {689 const config = {
690 RETURN_DOM: false,690 RETURN_DOM: false,
691 RETURN_DOM_FRAGMENT: false,691 RETURN_DOM_FRAGMENT: false,
@@ -1911,13 +1911,13 @@ export function addDOMPurifyHooks() {
1911 });1911 });
19121912
1913 DOMPurify.addHook('uponSanitizeAttribute', (node, data, config) => {1913 DOMPurify.addHook('uponSanitizeAttribute', (node, data, config) => {
1914 if (!config['MESSAGE_SANITIZE']) {1914 if (!config.MESSAGE_SANITIZE) {
1915 return;1915 return;
1916 }1916 }
19171917
1918 /* Retain the classes on UI elements of messages that interact with the main UI */1918 /* Retain the classes on UI elements of messages that interact with the main UI */
1919 const permittedNodeTypes = ['BUTTON', 'DIV'];1919 const permittedNodeTypes = ['BUTTON', 'DIV'];
1920 if (config['MESSAGE_ALLOW_SYSTEM_UI'] && node.classList.contains('menu_button') && permittedNodeTypes.includes(node.nodeName)) {1920 if (config.MESSAGE_ALLOW_SYSTEM_UI && node.classList.contains('menu_button') && permittedNodeTypes.includes(node.nodeName)) {
1921 return;1921 return;
1922 }1922 }
19231923
@@ -1938,7 +1938,7 @@ export function addDOMPurifyHooks() {
1938 });1938 });
19391939
1940 DOMPurify.addHook('uponSanitizeElement', (node, _, config) => {1940 DOMPurify.addHook('uponSanitizeElement', (node, _, config) => {
1941 if (!config['MESSAGE_SANITIZE']) {1941 if (!config.MESSAGE_SANITIZE) {
1942 return;1942 return;
1943 }1943 }
19441944
public/scripts/extensions/assets/index.js+15 -15
@@ -103,10 +103,10 @@ async function downloadAssetsList(url) {
103103
104 for (const i of json) {104 for (const i of json) {
105 //console.log(DEBUG_PREFIX,i)105 //console.log(DEBUG_PREFIX,i)
106 if (availableAssets[i['type']] === undefined)106 if (availableAssets[i.type] === undefined)
107 availableAssets[i['type']] = [];107 availableAssets[i.type] = [];
108108
109 availableAssets[i['type']].push(i);109 availableAssets[i.type].push(i);
110 }110 }
111111
112 console.debug(DEBUG_PREFIX, 'Updated available assets to', availableAssets);112 console.debug(DEBUG_PREFIX, 'Updated available assets to', availableAssets);
@@ -139,7 +139,7 @@ async function downloadAssetsList(url) {
139 assetTypeMenu.append(await renderExtensionTemplateAsync('assets', 'installation'));139 assetTypeMenu.append(await renderExtensionTemplateAsync('assets', 'installation'));
140 }140 }
141141
142 for (const asset of availableAssets[assetType].sort((a, b) => a?.name && b?.name && a['name'].localeCompare(b['name']))) {142 for (const asset of availableAssets[assetType].sort((a, b) => a?.name && b?.name && a.name.localeCompare(b.name))) {
143 const i = availableAssets[assetType].indexOf(asset);143 const i = availableAssets[assetType].indexOf(asset);
144 const elemId = `assets_install_${assetType}_${i}`;144 const elemId = `assets_install_${assetType}_${i}`;
145 let element = $('<div />', { id: elemId, class: 'asset-download-button right_menu_button' });145 let element = $('<div />', { id: elemId, class: 'asset-download-button right_menu_button' });
@@ -149,13 +149,13 @@ async function downloadAssetsList(url) {
149 //if (DEBUG_TONY_SAMA_FORK_MODE)149 //if (DEBUG_TONY_SAMA_FORK_MODE)
150 // asset["url"] = asset["url"].replace("https://github.com/SillyTavern/","https://github.com/Tony-sama/"); // DBG150 // asset["url"] = asset["url"].replace("https://github.com/SillyTavern/","https://github.com/Tony-sama/"); // DBG
151151
152 console.debug(DEBUG_PREFIX, 'Checking asset', asset['id'], asset['url']);152 console.debug(DEBUG_PREFIX, 'Checking asset', asset.id, asset.url);
153153
154 const assetInstall = async function () {154 const assetInstall = async function () {
155 element.off('click');155 element.off('click');
156 label.removeClass('fa-download');156 label.removeClass('fa-download');
157 this.classList.add('asset-download-button-loading');157 this.classList.add('asset-download-button-loading');
158 await installAsset(asset['url'], assetType, asset['id']);158 await installAsset(asset.url, assetType, asset.id);
159 label.addClass('fa-check');159 label.addClass('fa-check');
160 this.classList.remove('asset-download-button-loading');160 this.classList.remove('asset-download-button-loading');
161 element.on('click', assetDelete);161 element.on('click', assetDelete);
@@ -173,11 +173,11 @@ async function downloadAssetsList(url) {
173 const assetDelete = async function () {173 const assetDelete = async function () {
174 if (assetType === 'character') {174 if (assetType === 'character') {
175 toastr.error('Go to the characters menu to delete a character.', 'Character deletion not supported');175 toastr.error('Go to the characters menu to delete a character.', 'Character deletion not supported');
176 await executeSlashCommandsWithOptions(`/go ${asset['id']}`);176 await executeSlashCommandsWithOptions(`/go ${asset.id}`);
177 return;177 return;
178 }178 }
179 element.off('click');179 element.off('click');
180 await deleteAsset(assetType, asset['id']);180 await deleteAsset(assetType, asset.id);
181 label.removeClass('fa-check');181 label.removeClass('fa-check');
182 label.removeClass('redOverlayGlow');182 label.removeClass('redOverlayGlow');
183 label.removeClass('fa-trash');183 label.removeClass('fa-trash');
@@ -186,7 +186,7 @@ async function downloadAssetsList(url) {
186 element.on('click', assetInstall);186 element.on('click', assetInstall);
187 };187 };
188188
189 if (isAssetInstalled(assetType, asset['id'])) {189 if (isAssetInstalled(assetType, asset.id)) {
190 console.debug(DEBUG_PREFIX, 'installed, checked');190 console.debug(DEBUG_PREFIX, 'installed, checked');
191 label.toggleClass('fa-download');191 label.toggleClass('fa-download');
192 label.toggleClass('fa-check');192 label.toggleClass('fa-check');
@@ -207,14 +207,14 @@ async function downloadAssetsList(url) {
207 element.on('click', assetInstall);207 element.on('click', assetInstall);
208 }208 }
209209
210 console.debug(DEBUG_PREFIX, 'Created element for ', asset['id']);210 console.debug(DEBUG_PREFIX, 'Created element for ', asset.id);
211211
212 const displayName = DOMPurify.sanitize(asset['name'] || asset['id']);212 const displayName = DOMPurify.sanitize(asset.name || asset.id);
213 const description = DOMPurify.sanitize(asset['description'] || '');213 const description = DOMPurify.sanitize(asset.description || '');
214 const url = isValidUrl(asset['url']) ? asset['url'] : '';214 const url = isValidUrl(asset.url) ? asset.url : '';
215 const title = assetType === 'extension' ? t`Extension repo/guide:` + ` ${url}` : t`Preview in browser`;215 const title = assetType === 'extension' ? t`Extension repo/guide:` + ` ${url}` : t`Preview in browser`;
216 const previewIcon = (assetType === 'extension' || assetType === 'character') ? 'fa-arrow-up-right-from-square' : 'fa-headphones-simple';216 const previewIcon = (assetType === 'extension' || assetType === 'character') ? 'fa-arrow-up-right-from-square' : 'fa-headphones-simple';
217 const toolTag = assetType === 'extension' && asset['tool'];217 const toolTag = assetType === 'extension' && asset.tool;
218 const author = url && assetType === 'extension' ? getAuthorFromUrl(url) : EMPTY_AUTHOR;218 const author = url && assetType === 'extension' ? getAuthorFromUrl(url) : EMPTY_AUTHOR;
219219
220 const assetBlock = $('<i></i>')220 const assetBlock = $('<i></i>')
@@ -246,7 +246,7 @@ async function downloadAssetsList(url) {
246 if (asset.highlight) {246 if (asset.highlight) {
247 assetBlock.find('.asset-name').append('<i class="fa-solid fa-sm fa-trophy"></i>');247 assetBlock.find('.asset-name').append('<i class="fa-solid fa-sm fa-trophy"></i>');
248 }248 }
249 assetBlock.find('.asset-name').prepend(`<div class="avatar"><img src="${asset['url']}" alt="${displayName}"></div>`);249 assetBlock.find('.asset-name').prepend(`<div class="avatar"><img src="${asset.url}" alt="${displayName}"></div>`);
250 }250 }
251251
252 assetBlock.addClass('asset-block');252 assetBlock.addClass('asset-block');
public/scripts/extensions/caption/index.js+1 -1
@@ -204,7 +204,7 @@ async function sendCaptionedMessage(caption, image, mimeType) {
204 inline_image: !!extension_settings.caption.show_in_chat,204 inline_image: !!extension_settings.caption.show_in_chat,
205 },205 },
206 };206 };
207 chat_metadata['tainted'] = true;207 chat_metadata.tainted = true;
208 context.chat.push(message);208 context.chat.push(message);
209 const messageId = context.chat.length - 1;209 const messageId = context.chat.length - 1;
210 await eventSource.emit(event_types.MESSAGE_SENT, messageId);210 await eventSource.emit(event_types.MESSAGE_SENT, messageId);
public/scripts/extensions/memory/index.js+1 -1
@@ -456,7 +456,7 @@ async function onChatEvent() {
456 .catch(console.error)456 .catch(console.error)
457 .finally(() => {457 .finally(() => {
458 lastMessageId = context.chat?.length ?? null;458 lastMessageId = context.chat?.length ?? null;
459 lastMessageHash = getStringHash((context.chat.length && context.chat[context.chat.length - 1]['mes']) ?? '');459 lastMessageHash = getStringHash((context.chat.length && context.chat[context.chat.length - 1].mes) ?? '');
460 });460 });
461}461}
462462
public/scripts/extensions/quick-reply/index.js+1 -1
@@ -185,7 +185,7 @@ const init = async () => {
185 buttons.show();185 buttons.show();
186 settings.onSave = ()=>buttons.refresh();186 settings.onSave = ()=>buttons.refresh();
187187
188 window['executeQuickReplyByName'] = async(name, args = {}, options = {}) => {188 globalThis.executeQuickReplyByName = async(name, args = {}, options = {}) => {
189 let qr = [189 let qr = [
190 ...settings.config.setList,190 ...settings.config.setList,
191 ...(settings.chatConfig?.setList ?? []),191 ...(settings.chatConfig?.setList ?? []),
public/scripts/extensions/quick-reply/src/SlashCommandHandler.js+1 -1
@@ -77,7 +77,7 @@ export class SlashCommandHandler {
77 },77 },
78 };78 };
7979
80 window['qrEnumProviderExecutables'] = localEnumProviders.qrExecutables;80 globalThis.qrEnumProviderExecutables = localEnumProviders.qrExecutables;
8181
82 SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr',82 SlashCommandParser.addCommandObject(SlashCommand.fromProps({ name: 'qr',
83 callback: (_, value) => this.executeQuickReplyByIndex(Number(value)),83 callback: (_, value) => this.executeQuickReplyByIndex(Number(value)),
public/scripts/extensions/stable-diffusion/index.js+1 -1
@@ -426,7 +426,7 @@ function processTriggers(chat, _, abort, type) {
426 }426 }
427}427}
428428
429window['SD_ProcessTriggers'] = processTriggers;429globalThis.SD_ProcessTriggers = processTriggers;
430430
431function getSdRequestBody() {431function getSdRequestBody() {
432 switch (extension_settings.sd.source) {432 switch (extension_settings.sd.source) {
public/scripts/extensions/tts/coqui.js+10 -10
@@ -207,13 +207,13 @@ class CoquiTtsProvider {
207 this.settings.customVoices = {};207 this.settings.customVoices = {};
208 for (let voiceName in this.settings.voiceMapDict) {208 for (let voiceName in this.settings.voiceMapDict) {
209 const voiceId = this.settings.voiceMapDict[voiceName];209 const voiceId = this.settings.voiceMapDict[voiceName];
210 this.settings.customVoices[voiceName] = voiceId['model_id'];210 this.settings.customVoices[voiceName] = voiceId.model_id;
211211
212 if (voiceId['model_language'] != null)212 if (voiceId.model_language != null)
213 this.settings.customVoices[voiceName] += '[' + voiceId['model_language'] + ']';213 this.settings.customVoices[voiceName] += '[' + voiceId.model_language + ']';
214214
215 if (voiceId['model_speaker'] != null)215 if (voiceId.model_speaker != null)
216 this.settings.customVoices[voiceName] += '[' + voiceId['model_speaker'] + ']';216 this.settings.customVoices[voiceName] += '[' + voiceId.model_speaker + ']';
217 }217 }
218218
219 // Update UI select list with voices219 // Update UI select list with voices
@@ -493,8 +493,8 @@ class CoquiTtsProvider {
493 .append('<option value="none">Select language</option>')493 .append('<option value="none">Select language</option>')
494 .val('none');494 .val('none');
495495
496 for (let i = 0; i < model_settings['languages'].length; i++) {496 for (let i = 0; i < model_settings.languages.length; i++) {
497 const language_label = JSON.stringify(model_settings['languages'][i]).replaceAll('"', '');497 const language_label = JSON.stringify(model_settings.languages[i]).replaceAll('"', '');
498 $('#coqui_api_model_settings_language').append(new Option(language_label, i));498 $('#coqui_api_model_settings_language').append(new Option(language_label, i));
499 }499 }
500 }500 }
@@ -512,8 +512,8 @@ class CoquiTtsProvider {
512 .append('<option value="none">Select speaker</option>')512 .append('<option value="none">Select speaker</option>')
513 .val('none');513 .val('none');
514514
515 for (let i = 0; i < model_settings['speakers'].length; i++) {515 for (let i = 0; i < model_settings.speakers.length; i++) {
516 const speaker_label = JSON.stringify(model_settings['speakers'][i]).replaceAll('"', '');516 const speaker_label = JSON.stringify(model_settings.speakers[i]).replaceAll('"', '');
517 $('#coqui_api_model_settings_speaker').append(new Option(speaker_label, i));517 $('#coqui_api_model_settings_speaker').append(new Option(speaker_label, i));
518 }518 }
519 }519 }
@@ -525,7 +525,7 @@ class CoquiTtsProvider {
525 $('#coqui_api_model_install_status').show();525 $('#coqui_api_model_install_status').show();
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 let result = await CoquiTtsProvider.checkmodel_state(model_id);530 let result = await CoquiTtsProvider.checkmodel_state(model_id);
531 result = await result.json();531 result = await result.json();
public/scripts/extensions/tts/cosyvoice.js+1 -1
@@ -175,7 +175,7 @@ class CosyVoiceProvider {
175 };175 };
176176
177 if (streaming) {177 if (streaming) {
178 params['streaming'] = 1;178 params.streaming = 1;
179 }179 }
180180
181 const url = `${this.settings.provider_endpoint}/`;181 const url = `${this.settings.provider_endpoint}/`;
public/scripts/extensions/tts/elevenlabs.js+3 -3
@@ -126,16 +126,16 @@ class ElevenLabsTtsProvider {
126 this.settings = this.defaultSettings;126 this.settings = this.defaultSettings;
127127
128 // Migrate old settings128 // Migrate old settings
129 if (settings['multilingual'] !== undefined) {129 if (settings.multilingual !== undefined) {
130 settings.model = settings.multilingual ? 'eleven_multilingual_v1' : 'eleven_monolingual_v1';130 settings.model = settings.multilingual ? 'eleven_multilingual_v1' : 'eleven_monolingual_v1';
131 delete settings['multilingual'];131 delete settings.multilingual;
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;
139 }139 }
140140
141 $('#elevenlabs_tts_key').toggleClass('success', !!secret_state[SECRET_KEYS.ELEVENLABS]);141 $('#elevenlabs_tts_key').toggleClass('success', !!secret_state[SECRET_KEYS.ELEVENLABS]);
public/scripts/extensions/tts/index.js+7 -7
@@ -284,7 +284,7 @@ function debugTtsPlayback() {
284 },284 },
285 ));285 ));
286}286}
287window['debugTtsPlayback'] = debugTtsPlayback;287globalThis.debugTtsPlayback = debugTtsPlayback;
288288
289//##################//289//##################//
290// Audio Control //290// Audio Control //
@@ -321,8 +321,8 @@ async function playAudioData(audioJob) {
321 const srcUrl = await getBase64Async(audioBlob);321 const srcUrl = await getBase64Async(audioBlob);
322322
323 // VRM lip sync323 // VRM lip sync
324 if (extension_settings.vrm?.enabled && typeof window['vrmLipSync'] === 'function') {324 if (extension_settings.vrm?.enabled && typeof globalThis.vrmLipSync === 'function') {
325 await window['vrmLipSync'](audioBlob, char);325 await globalThis.vrmLipSync(audioBlob, char);
326 }326 }
327327
328 audioElement.src = srcUrl;328 audioElement.src = srcUrl;
@@ -339,7 +339,7 @@ async function playAudioData(audioJob) {
339 });339 });
340}340}
341341
342window['tts_preview'] = function (id) {342globalThis.tts_preview = function (id) {
343 const audio = document.getElementById(id);343 const audio = document.getElementById(id);
344344
345 if (audio instanceof HTMLAudioElement && !$(audio).data('disabled')) {345 if (audio instanceof HTMLAudioElement && !$(audio).data('disabled')) {
@@ -474,8 +474,8 @@ function completeTtsJob() {
474async function tts(text, voiceId, char, voiceMapKey = null) {474async function tts(text, voiceId, char, voiceMapKey = null) {
475 async function processResponse(response) {475 async function processResponse(response) {
476 // RVC injection476 // RVC injection
477 if (typeof window['rvcVoiceConversion'] === 'function' && extension_settings.rvc.enabled)477 if (typeof globalThis.rvcVoiceConversion === 'function' && extension_settings.rvc.enabled)
478 response = await window['rvcVoiceConversion'](response, char, text);478 response = await globalThis.rvcVoiceConversion(response, char, text);
479479
480 await addAudioJob(response, char);480 await addAudioJob(response, char);
481 }481 }
@@ -806,7 +806,7 @@ async function playFullConversation() {
806 ttsJobQueue = chat;806 ttsJobQueue = chat;
807}807}
808808
809window['playFullConversation'] = playFullConversation;809globalThis.playFullConversation = playFullConversation;
810810
811//#############################//811//#############################//
812// Extension UI and Settings //812// Extension UI and Settings //
public/scripts/extensions/vectors/index.js+2 -2
@@ -746,7 +746,7 @@ function overlapChunks(chunk, index, chunks, overlapSize) {
746 return overlappedChunk;746 return overlappedChunk;
747}747}
748748
749window['vectors_rearrangeChat'] = rearrangeChat;749globalThis.vectors_rearrangeChat = rearrangeChat;
750750
751const onChatEvent = debounce(async () => await moduleWorker.update(), debounce_timeout.relaxed);751const onChatEvent = debounce(async () => await moduleWorker.update(), debounce_timeout.relaxed);
752752
@@ -1620,7 +1620,7 @@ jQuery(async () => {
1620 }1620 }
16211621
1622 // Migrate from old settings1622 // Migrate from old settings
1623 if (settings['enabled']) {1623 if (settings.enabled) {
1624 settings.enabled_chats = true;1624 settings.enabled_chats = true;
1625 }1625 }
16261626
public/scripts/group-chats.js+14 -14
@@ -274,8 +274,8 @@ export async function getGroupChat(groupId, reload = false) {
274 }274 }
275275
276 // Add integrity slug if missing276 // Add integrity slug if missing
277 if (!metadata['integrity']) {277 if (!metadata.integrity) {
278 metadata['integrity'] = uuidv4();278 metadata.integrity = uuidv4();
279 }279 }
280280
281 await loadItemizedPrompts(getCurrentChatId());281 await loadItemizedPrompts(getCurrentChatId());
@@ -559,8 +559,8 @@ export function getGroupCharacterCardsLazy(groupId, characterId) {
559 return values.filter(x => x.length).join('\n');559 return values.filter(x => x.length).join('\n');
560 }560 }
561561
562 const scenarioOverride = String(chat_metadata['scenario'] || '');562 const scenarioOverride = String(chat_metadata.scenario || '');
563 const mesExamplesOverride = String(chat_metadata['mes_example'] || '');563 const mesExamplesOverride = String(chat_metadata.mes_example || '');
564564
565 return createLazyFields({565 return createLazyFields({
566 description: () => collectField('Description', c => c.description),566 description: () => collectField('Description', c => c.description),
@@ -593,16 +593,16 @@ async function getFirstCharacterMessage(character) {
593 }593 }
594594
595 const mes = {};595 const mes = {};
596 mes['is_user'] = false;596 mes.is_user = false;
597 mes['is_system'] = false;597 mes.is_system = false;
598 mes['name'] = character.name;598 mes.name = character.name;
599 mes['send_date'] = getMessageTimeStamp();599 mes.send_date = getMessageTimeStamp();
600 mes['original_avatar'] = character.avatar;600 mes.original_avatar = character.avatar;
601 mes['extra'] = { 'gen_id': Date.now() * Math.random() * 1000000 };601 mes.extra = { 'gen_id': Date.now() * Math.random() * 1000000 };
602 mes['mes'] = messageText602 mes.mes = messageText
603 ? substituteParams(messageText.trim(), { name2Override: character.name })603 ? substituteParams(messageText.trim(), { name2Override: character.name })
604 : '';604 : '';
605 mes['force_avatar'] =605 mes.force_avatar =
606 character.avatar != 'none'606 character.avatar != 'none'
607 ? getThumbnailUrl('avatar', character.avatar)607 ? getThumbnailUrl('avatar', character.avatar)
608 : default_avatar;608 : default_avatar;
@@ -628,7 +628,7 @@ async function saveGroupChat(groupId, shouldSaveGroup, force = false) {
628 return;628 return;
629 }629 }
630 const chatId = group.chat_id;630 const chatId = group.chat_id;
631 group['date_last_chat'] = Date.now();631 group.date_last_chat = Date.now();
632 /** @type {ChatHeader} */632 /** @type {ChatHeader} */
633 const chatHeader = {633 const chatHeader = {
634 chat_metadata: { ...chat_metadata },634 chat_metadata: { ...chat_metadata },
@@ -2179,7 +2179,7 @@ export async function openGroupChat(groupId, chatId) {
2179 await clearChat();2179 await clearChat();
2180 chat.length = 0;2180 chat.length = 0;
2181 group.chat_id = chatId;2181 group.chat_id = chatId;
2182 group['date_last_chat'] = Date.now();2182 group.date_last_chat = Date.now();
2183 updateChatMetadata({}, true);2183 updateChatMetadata({}, true);
21842184
2185 await editGroup(groupId, true, false);2185 await editGroup(groupId, true, false);
public/scripts/horde.js+5 -5
@@ -205,11 +205,11 @@ export async function generateHorde(prompt, params, signal, reportProgress) {
205 delete params.prompt;205 delete params.prompt;
206206
207 // No idea what these do207 // No idea what these do
208 params['n'] = 1;208 params.n = 1;
209 params['frmtadsnsp'] = false;209 params.frmtadsnsp = false;
210 params['frmtrmblln'] = false;210 params.frmtrmblln = false;
211 params['frmtrmspch'] = false;211 params.frmtrmspch = false;
212 params['frmttriminc'] = false;212 params.frmttriminc = false;
213213
214 const payload = {214 const payload = {
215 'prompt': prompt,215 'prompt': prompt,
public/scripts/i18n.js+2 -2
@@ -258,8 +258,8 @@ function addLanguagesToDropdown() {
258 const uiLanguageSelects = $('#ui_language_select, #onboarding_ui_language_select');258 const uiLanguageSelects = $('#ui_language_select, #onboarding_ui_language_select');
259 for (const langObj of langs) { // Set the value to the language code259 for (const langObj of langs) { // Set the value to the language code
260 const option = document.createElement('option');260 const option = document.createElement('option');
261 option.value = langObj['lang']; // Set the value to the language code261 option.value = langObj.lang; // Set the value to the language code
262 option.innerText = langObj['display']; // Set the display text to the language name262 option.innerText = langObj.display; // Set the display text to the language name
263 uiLanguageSelects.append(option);263 uiLanguageSelects.append(option);
264 }264 }
265265
public/scripts/instruct-mode.js+1 -1
@@ -161,7 +161,7 @@ export async function loadInstructMode(data) {
161 */161 */
162export function updateBindModelTemplatesState() {162export function updateBindModelTemplatesState() {
163 const bindModelTemplates = power_user.model_templates_mappings[online_status] ?? power_user.model_templates_mappings[power_user.chat_template_hash];163 const bindModelTemplates = power_user.model_templates_mappings[online_status] ?? power_user.model_templates_mappings[power_user.chat_template_hash];
164 const bindingsMatch = (bindModelTemplates && power_user.context.preset === bindModelTemplates['context'] && (!power_user.instruct.enabled || power_user.instruct.preset === bindModelTemplates['instruct'])) ?? false;164 const bindingsMatch = (bindModelTemplates && power_user.context.preset === bindModelTemplates.context && (!power_user.instruct.enabled || power_user.instruct.preset === bindModelTemplates.instruct)) ?? false;
165 const currentState = $('#bind_model_templates').prop('checked');165 const currentState = $('#bind_model_templates').prop('checked');
166 if (bindingsMatch === currentState) {166 if (bindingsMatch === currentState) {
167 // No change needed167 // No change needed
public/scripts/macros.js+4 -4
@@ -312,14 +312,14 @@ export class MacrosParser {
312 * @returns {number} The hashed chat id312 * @returns {number} The hashed chat id
313 */313 */
314function getChatIdHash() {314function getChatIdHash() {
315 const cachedIdHash = chat_metadata['chat_id_hash'];315 const cachedIdHash = chat_metadata.chat_id_hash;
316316
317 // If chat_id_hash is not already set, calculate it317 // If chat_id_hash is not already set, calculate it
318 if (!cachedIdHash) {318 if (!cachedIdHash) {
319 // Use the main_chat if it's available, otherwise get the current chat ID319 // Use the main_chat if it's available, otherwise get the current chat ID
320 const chatId = chat_metadata['main_chat'] ?? getCurrentChatId();320 const chatId = chat_metadata.main_chat ?? getCurrentChatId();
321 const chatIdHash = getStringHash(chatId);321 const chatIdHash = getStringHash(chatId);
322 chat_metadata['chat_id_hash'] = chatIdHash;322 chat_metadata.chat_id_hash = chatIdHash;
323 return chatIdHash;323 return chatIdHash;
324 }324 }
325325
@@ -361,7 +361,7 @@ export function getLastMessageId({ exclude_swipe_in_propress = true, filter = nu
361 * @returns {number|null} The ID of the first message in the context361 * @returns {number|null} The ID of the first message in the context
362 */362 */
363function getFirstIncludedMessageId() {363function getFirstIncludedMessageId() {
364 return chat_metadata['lastInContextMessageId'];364 return chat_metadata.lastInContextMessageId;
365}365}
366366
367/**367/**
public/scripts/macros/definitions/chat-macros.js+1 -1
@@ -104,7 +104,7 @@ function getLastCharMessage() {
104}104}
105105
106function getFirstIncludedMessageId() {106function getFirstIncludedMessageId() {
107 const value = chat_metadata['lastInContextMessageId'];107 const value = chat_metadata.lastInContextMessageId;
108 return typeof value === 'number' ? value : null;108 return typeof value === 'number' ? value : null;
109}109}
110110
public/scripts/macros/definitions/core-macros.js+3 -3
@@ -433,13 +433,13 @@ export function registerCoreMacros() {
433}433}
434434
435function getChatIdHash() {435function getChatIdHash() {
436 const cachedIdHash = chat_metadata['chat_id_hash'];436 const cachedIdHash = chat_metadata.chat_id_hash;
437 if (typeof cachedIdHash === 'number') {437 if (typeof cachedIdHash === 'number') {
438 return cachedIdHash;438 return cachedIdHash;
439 }439 }
440440
441 const chatId = chat_metadata['main_chat'] ?? getCurrentChatId();441 const chatId = chat_metadata.main_chat ?? getCurrentChatId();
442 const chatIdHash = getStringHash(chatId);442 const chatIdHash = getStringHash(chatId);
443 chat_metadata['chat_id_hash'] = chatIdHash;443 chat_metadata.chat_id_hash = chatIdHash;
444 return chatIdHash;444 return chatIdHash;
445}445}
public/scripts/openai.js+53 -53
@@ -529,8 +529,8 @@ function setOpenAIMessages(chat) {
529 const currentModel = getChatCompletionModel();529 const currentModel = getChatCompletionModel();
530530
531 for (let i = chat.length - 1; i >= 0; i--) {531 for (let i = chat.length - 1; i >= 0; i--) {
532 let role = chat[j]['is_user'] ? 'user' : 'assistant';532 let role = chat[j].is_user ? 'user' : 'assistant';
533 let content = chat[j]['mes'];533 let content = chat[j].mes;
534534
535 // If this symbol flag is set, completely ignore the message.535 // If this symbol flag is set, completely ignore the message.
536 // This can be used to hide messages without affecting the number of messages in the chat.536 // This can be used to hide messages without affecting the number of messages in the chat.
@@ -567,7 +567,7 @@ function setOpenAIMessages(chat) {
567 // remove caret return (waste of tokens)567 // remove caret return (waste of tokens)
568 content = content.replace(/\r/gm, '');568 content = content.replace(/\r/gm, '');
569569
570 const name = chat[j]['name'];570 const name = chat[j].name;
571 const media = chat[j]?.extra?.media;571 const media = chat[j]?.extra?.media;
572 const mediaDisplay = getMediaDisplay(chat[j]);572 const mediaDisplay = getMediaDisplay(chat[j]);
573 const mediaIndex = getMediaIndex(chat[j]);573 const mediaIndex = getMediaIndex(chat[j]);
@@ -2588,14 +2588,14 @@ export async function createGenerationParameters(settings, model, type, messages
25882588
2589 if (settings.reverse_proxy && proxySupportedSources.includes(settings.chat_completion_source)) {2589 if (settings.reverse_proxy && proxySupportedSources.includes(settings.chat_completion_source)) {
2590 await validateReverseProxy();2590 await validateReverseProxy();
2591 generate_data['reverse_proxy'] = settings.reverse_proxy;2591 generate_data.reverse_proxy = settings.reverse_proxy;
2592 generate_data['proxy_password'] = settings.proxy_password;2592 generate_data.proxy_password = settings.proxy_password;
2593 }2593 }
25942594
2595 // Add logprobs request (max 5 per OpenAI docs)2595 // Add logprobs request (max 5 per OpenAI docs)
2596 const useLogprobs = !!power_user.request_token_probabilities;2596 const useLogprobs = !!power_user.request_token_probabilities;
2597 if (useLogprobs && logprobsSupportedSources.includes(settings.chat_completion_source)) {2597 if (useLogprobs && logprobsSupportedSources.includes(settings.chat_completion_source)) {
2598 generate_data['logprobs'] = 5;2598 generate_data.logprobs = 5;
2599 }2599 }
26002600
2601 // Remove logit bias/logprobs/stop-strings if not supported by the model2601 // Remove logit bias/logprobs/stop-strings if not supported by the model
@@ -2610,67 +2610,67 @@ export async function createGenerationParameters(settings, model, type, messages
2610 }2610 }
26112611
2612 if (settings.chat_completion_source === chat_completion_sources.CLAUDE) {2612 if (settings.chat_completion_source === chat_completion_sources.CLAUDE) {
2613 generate_data['top_k'] = Number(settings.top_k_openai);2613 generate_data.top_k = Number(settings.top_k_openai);
2614 generate_data['use_sysprompt'] = settings.use_sysprompt;2614 generate_data.use_sysprompt = settings.use_sysprompt;
2615 generate_data['stop'] = getCustomStoppingStrings(); // Claude shouldn't have limits on stop strings.2615 generate_data.stop = getCustomStoppingStrings(); // Claude shouldn't have limits on stop strings.
2616 // Don't add a prefill on quiet gens (summarization) and when using continue prefill.2616 // Don't add a prefill on quiet gens (summarization) and when using continue prefill.
2617 if (type !== 'quiet' && !(type === 'continue' && settings.continue_prefill)) {2617 if (type !== 'quiet' && !(type === 'continue' && settings.continue_prefill)) {
2618 generate_data['assistant_prefill'] = type === 'impersonate'2618 generate_data.assistant_prefill = type === 'impersonate'
2619 ? substituteParams(settings.assistant_impersonation)2619 ? substituteParams(settings.assistant_impersonation)
2620 : substituteParams(settings.assistant_prefill);2620 : substituteParams(settings.assistant_prefill);
2621 }2621 }
2622 }2622 }
26232623
2624 if (settings.chat_completion_source === chat_completion_sources.OPENROUTER) {2624 if (settings.chat_completion_source === chat_completion_sources.OPENROUTER) {
2625 generate_data['top_k'] = Number(settings.top_k_openai);2625 generate_data.top_k = Number(settings.top_k_openai);
2626 generate_data['min_p'] = Number(settings.min_p_openai);2626 generate_data.min_p = Number(settings.min_p_openai);
2627 generate_data['repetition_penalty'] = Number(settings.repetition_penalty_openai);2627 generate_data.repetition_penalty = Number(settings.repetition_penalty_openai);
2628 generate_data['top_a'] = Number(settings.top_a_openai);2628 generate_data.top_a = Number(settings.top_a_openai);
2629 generate_data['use_fallback'] = settings.openrouter_use_fallback;2629 generate_data.use_fallback = settings.openrouter_use_fallback;
2630 generate_data['provider'] = settings.openrouter_providers;2630 generate_data.provider = settings.openrouter_providers;
2631 generate_data['allow_fallbacks'] = settings.openrouter_allow_fallbacks;2631 generate_data.allow_fallbacks = settings.openrouter_allow_fallbacks;
2632 generate_data['middleout'] = settings.openrouter_middleout;2632 generate_data.middleout = settings.openrouter_middleout;
2633 }2633 }
26342634
2635 if ([chat_completion_sources.MAKERSUITE, chat_completion_sources.VERTEXAI].includes(settings.chat_completion_source)) {2635 if ([chat_completion_sources.MAKERSUITE, chat_completion_sources.VERTEXAI].includes(settings.chat_completion_source)) {
2636 const stopStringsLimit = 5;2636 const stopStringsLimit = 5;
2637 generate_data['top_k'] = Number(settings.top_k_openai);2637 generate_data.top_k = Number(settings.top_k_openai);
2638 generate_data['stop'] = getCustomStoppingStrings(stopStringsLimit).slice(0, stopStringsLimit).filter(x => x.length >= 1 && x.length <= 16);2638 generate_data.stop = getCustomStoppingStrings(stopStringsLimit).slice(0, stopStringsLimit).filter(x => x.length >= 1 && x.length <= 16);
2639 generate_data['use_sysprompt'] = settings.use_sysprompt;2639 generate_data.use_sysprompt = settings.use_sysprompt;
2640 if (settings.chat_completion_source === chat_completion_sources.VERTEXAI) {2640 if (settings.chat_completion_source === chat_completion_sources.VERTEXAI) {
2641 generate_data['vertexai_auth_mode'] = settings.vertexai_auth_mode;2641 generate_data.vertexai_auth_mode = settings.vertexai_auth_mode;
2642 generate_data['vertexai_region'] = settings.vertexai_region;2642 generate_data.vertexai_region = settings.vertexai_region;
2643 generate_data['vertexai_express_project_id'] = settings.vertexai_express_project_id;2643 generate_data.vertexai_express_project_id = settings.vertexai_express_project_id;
2644 }2644 }
2645 }2645 }
26462646
2647 if (settings.chat_completion_source === chat_completion_sources.MISTRALAI) {2647 if (settings.chat_completion_source === chat_completion_sources.MISTRALAI) {
2648 generate_data['safe_prompt'] = false; // already defaults to false, but just incase they change that in the future.2648 generate_data.safe_prompt = false; // already defaults to false, but just incase they change that in the future.
2649 generate_data['stop'] = getCustomStoppingStrings(); // Mistral shouldn't have limits on stop strings.2649 generate_data.stop = getCustomStoppingStrings(); // Mistral shouldn't have limits on stop strings.
2650 }2650 }
26512651
2652 if (settings.chat_completion_source === chat_completion_sources.CUSTOM) {2652 if (settings.chat_completion_source === chat_completion_sources.CUSTOM) {
2653 generate_data['custom_url'] = settings.custom_url;2653 generate_data.custom_url = settings.custom_url;
2654 generate_data['custom_include_body'] = settings.custom_include_body;2654 generate_data.custom_include_body = settings.custom_include_body;
2655 generate_data['custom_exclude_body'] = settings.custom_exclude_body;2655 generate_data.custom_exclude_body = settings.custom_exclude_body;
2656 generate_data['custom_include_headers'] = settings.custom_include_headers;2656 generate_data.custom_include_headers = settings.custom_include_headers;
2657 }2657 }
26582658
2659 if (settings.chat_completion_source === chat_completion_sources.COHERE) {2659 if (settings.chat_completion_source === chat_completion_sources.COHERE) {
2660 // Clamp to 0.01 -> 0.992660 // Clamp to 0.01 -> 0.99
2661 generate_data['top_p'] = Math.min(Math.max(Number(settings.top_p_openai), 0.01), 0.99);2661 generate_data.top_p = Math.min(Math.max(Number(settings.top_p_openai), 0.01), 0.99);
2662 generate_data['top_k'] = Number(settings.top_k_openai);2662 generate_data.top_k = Number(settings.top_k_openai);
2663 // Clamp to 0 -> 12663 // Clamp to 0 -> 1
2664 generate_data['frequency_penalty'] = Math.min(Math.max(Number(settings.freq_pen_openai), 0), 1);2664 generate_data.frequency_penalty = Math.min(Math.max(Number(settings.freq_pen_openai), 0), 1);
2665 generate_data['presence_penalty'] = Math.min(Math.max(Number(settings.pres_pen_openai), 0), 1);2665 generate_data.presence_penalty = Math.min(Math.max(Number(settings.pres_pen_openai), 0), 1);
2666 generate_data['stop'] = getCustomStoppingStrings(5);2666 generate_data.stop = getCustomStoppingStrings(5);
2667 }2667 }
26682668
2669 if (settings.chat_completion_source === chat_completion_sources.PERPLEXITY) {2669 if (settings.chat_completion_source === chat_completion_sources.PERPLEXITY) {
2670 generate_data['top_k'] = Number(settings.top_k_openai);2670 generate_data.top_k = Number(settings.top_k_openai);
2671 generate_data['frequency_penalty'] = Number(settings.freq_pen_openai);2671 generate_data.frequency_penalty = Number(settings.freq_pen_openai);
2672 generate_data['presence_penalty'] = Number(settings.pres_pen_openai);2672 generate_data.presence_penalty = Number(settings.pres_pen_openai);
2673 delete generate_data['stop'];2673 delete generate_data.stop;
2674 }2674 }
26752675
2676 // https://console.groq.com/docs/openai2676 // https://console.groq.com/docs/openai
@@ -2713,35 +2713,35 @@ export async function createGenerationParameters(settings, model, type, messages
27132713
2714 // https://docs.electronhub.ai/api-reference/chat/completions2714 // https://docs.electronhub.ai/api-reference/chat/completions
2715 if (settings.chat_completion_source === chat_completion_sources.ELECTRONHUB) {2715 if (settings.chat_completion_source === chat_completion_sources.ELECTRONHUB) {
2716 generate_data['top_k'] = Number(settings.top_k_openai);2716 generate_data.top_k = Number(settings.top_k_openai);
2717 }2717 }
27182718
2719 if (settings.chat_completion_source === chat_completion_sources.CHUTES) {2719 if (settings.chat_completion_source === chat_completion_sources.CHUTES) {
2720 generate_data['min_p'] = Number(settings.min_p_openai);2720 generate_data.min_p = Number(settings.min_p_openai);
2721 generate_data['top_k'] = settings.top_k_openai > 0 ? Number(settings.top_k_openai) : undefined;2721 generate_data.top_k = settings.top_k_openai > 0 ? Number(settings.top_k_openai) : undefined;
2722 generate_data['repetition_penalty'] = Number(settings.repetition_penalty_openai);2722 generate_data.repetition_penalty = Number(settings.repetition_penalty_openai);
2723 generate_data['stop'] = getCustomStoppingStrings();2723 generate_data.stop = getCustomStoppingStrings();
2724 }2724 }
27252725
2726 // https://docs.z.ai/api-reference/llm/chat-completion2726 // https://docs.z.ai/api-reference/llm/chat-completion
2727 if (settings.chat_completion_source === chat_completion_sources.ZAI) {2727 if (settings.chat_completion_source === chat_completion_sources.ZAI) {
2728 generate_data['top_p'] = generate_data.top_p || 0.01;2728 generate_data.top_p = generate_data.top_p || 0.01;
2729 generate_data['stop'] = getCustomStoppingStrings(1);2729 generate_data.stop = getCustomStoppingStrings(1);
2730 generate_data['zai_endpoint'] = settings.zai_endpoint || ZAI_ENDPOINT.COMMON;2730 generate_data.zai_endpoint = settings.zai_endpoint || ZAI_ENDPOINT.COMMON;
2731 delete generate_data.presence_penalty;2731 delete generate_data.presence_penalty;
2732 delete generate_data.frequency_penalty;2732 delete generate_data.frequency_penalty;
2733 }2733 }
27342734
2735 // https://docs.nano-gpt.com/api-reference/endpoint/chat-completion#temperature-&-nucleus2735 // https://docs.nano-gpt.com/api-reference/endpoint/chat-completion#temperature-&-nucleus
2736 if (settings.chat_completion_source === chat_completion_sources.NANOGPT) {2736 if (settings.chat_completion_source === chat_completion_sources.NANOGPT) {
2737 generate_data['top_k'] = Number(settings.top_k_openai);2737 generate_data.top_k = Number(settings.top_k_openai);
2738 generate_data['min_p'] = Number(settings.min_p_openai);2738 generate_data.min_p = Number(settings.min_p_openai);
2739 generate_data['repetition_penalty'] = Number(settings.repetition_penalty_openai);2739 generate_data.repetition_penalty = Number(settings.repetition_penalty_openai);
2740 generate_data['top_a'] = Number(settings.top_a_openai);2740 generate_data.top_a = Number(settings.top_a_openai);
2741 }2741 }
27422742
2743 if (seedSupportedSources.includes(settings.chat_completion_source) && settings.seed >= 0) {2743 if (seedSupportedSources.includes(settings.chat_completion_source) && settings.seed >= 0) {
2744 generate_data['seed'] = settings.seed;2744 generate_data.seed = settings.seed;
2745 }2745 }
27462746
2747 if ([chat_completion_sources.OPENAI, chat_completion_sources.AZURE_OPENAI].includes(settings.chat_completion_source) && /^(o1|o3|o4)/.test(model) ||2747 if ([chat_completion_sources.OPENAI, chat_completion_sources.AZURE_OPENAI].includes(settings.chat_completion_source) && /^(o1|o3|o4)/.test(model) ||
@@ -6162,7 +6162,7 @@ async function onVertexAIValidateServiceAccount() {
6162 }6162 }
61636163
6164 // Save to backend secret storage6164 // Save to backend secret storage
6165 const keyLabel = serviceAccount['client_email'] || '';6165 const keyLabel = serviceAccount.client_email || '';
6166 await writeSecret(SECRET_KEYS.VERTEXAI_SERVICE_ACCOUNT, jsonContent, keyLabel);6166 await writeSecret(SECRET_KEYS.VERTEXAI_SERVICE_ACCOUNT, jsonContent, keyLabel);
61676167
6168 // Show success status6168 // Show success status
public/scripts/personas.js+20 -20
@@ -835,7 +835,7 @@ async function renamePersona(avatarId) {
835async function selectCurrentPersona({ toastPersonaNameChange = true } = {}) {835async function selectCurrentPersona({ toastPersonaNameChange = true } = {}) {
836 const personaName = power_user.personas[user_avatar];836 const personaName = power_user.personas[user_avatar];
837 if (personaName) {837 if (personaName) {
838 const shouldAutoLock = power_user.persona_auto_lock && user_avatar !== chat_metadata['persona'];838 const shouldAutoLock = power_user.persona_auto_lock && user_avatar !== chat_metadata.persona;
839839
840 if (personaName !== name1) {840 if (personaName !== name1) {
841 console.log(`Auto-updating user name to ${personaName}`);841 console.log(`Auto-updating user name to ${personaName}`);
@@ -871,7 +871,7 @@ async function selectCurrentPersona({ toastPersonaNameChange = true } = {}) {
871871
872 // Update the locked persona if setting is enabled872 // Update the locked persona if setting is enabled
873 if (shouldAutoLock) {873 if (shouldAutoLock) {
874 chat_metadata['persona'] = user_avatar;874 chat_metadata.persona = user_avatar;
875 console.log(`Auto locked persona to ${user_avatar}`);875 console.log(`Auto locked persona to ${user_avatar}`);
876 if (toastPersonaNameChange && power_user.persona_show_notifications) {876 if (toastPersonaNameChange && power_user.persona_show_notifications) {
877 toastr.success(t`Persona ${personaName} selected and auto-locked to current chat`, t`Persona Selected`);877 toastr.success(t`Persona ${personaName} selected and auto-locked to current chat`, t`Persona Selected`);
@@ -914,7 +914,7 @@ export function isPersonaLocked(type = 'chat') {
914 case 'default':914 case 'default':
915 return power_user.default_persona === user_avatar;915 return power_user.default_persona === user_avatar;
916 case 'chat':916 case 'chat':
917 return chat_metadata['persona'] == user_avatar;917 return chat_metadata.persona == user_avatar;
918 case 'character': {918 case 'character': {
919 return !!power_user.persona_descriptions[user_avatar]?.connections?.some(isPersonaConnectionLocked);919 return !!power_user.persona_descriptions[user_avatar]?.connections?.some(isPersonaConnectionLocked);
920 }920 }
@@ -960,9 +960,9 @@ async function unlockPersona(type = 'chat') {
960 break;960 break;
961 }961 }
962 case 'chat': {962 case 'chat': {
963 if (chat_metadata['persona']) {963 if (chat_metadata.persona) {
964 console.log(`Unlocking persona ${user_avatar} from this chat`);964 console.log(`Unlocking persona ${user_avatar} from this chat`);
965 delete chat_metadata['persona'];965 delete chat_metadata.persona;
966 await saveMetadata();966 await saveMetadata();
967 if (power_user.persona_show_notifications && !isPersonaPanelOpen()) {967 if (power_user.persona_show_notifications && !isPersonaPanelOpen()) {
968 toastr.info(t`Persona ${name1} is now unlocked from this chat.`, t`Persona Unlocked`);968 toastr.info(t`Persona ${name1} is now unlocked from this chat.`, t`Persona Unlocked`);
@@ -1021,7 +1021,7 @@ async function lockPersona(type = 'chat') {
1021 }1021 }
1022 case 'chat': {1022 case 'chat': {
1023 console.log(`Locking persona ${user_avatar} to this chat`);1023 console.log(`Locking persona ${user_avatar} to this chat`);
1024 chat_metadata['persona'] = user_avatar;1024 chat_metadata.persona = user_avatar;
1025 saveMetadataDebounced();1025 saveMetadataDebounced();
1026 if (power_user.persona_show_notifications && !isPersonaPanelOpen()) {1026 if (power_user.persona_show_notifications && !isPersonaPanelOpen()) {
1027 toastr.success(t`User persona ${name1} is locked to ${name2} in this chat`, t`Persona Locked`);1027 toastr.success(t`User persona ${name1} is locked to ${name2} in this chat`, t`Persona Locked`);
@@ -1105,9 +1105,9 @@ async function deleteUserAvatar() {
1105 power_user.default_persona = null;1105 power_user.default_persona = null;
1106 }1106 }
11071107
1108 if (avatarId === chat_metadata['persona']) {1108 if (avatarId === chat_metadata.persona) {
1109 toastr.warning(t`The locked persona was deleted. You will need to set a new persona for this chat.`, t`Persona Deleted`);1109 toastr.warning(t`The locked persona was deleted. You will need to set a new persona for this chat.`, t`Persona Deleted`);
1110 delete chat_metadata['persona'];1110 delete chat_metadata.persona;
1111 await saveMetadata();1111 await saveMetadata();
1112 }1112 }
11131113
@@ -1317,7 +1317,7 @@ async function toggleDefaultPersona(avatarId, { quiet = false } = {}) {
1317 */1317 */
1318function getPersonaStates(avatarId) {1318function getPersonaStates(avatarId) {
1319 const isDefaultPersona = power_user.default_persona === avatarId;1319 const isDefaultPersona = power_user.default_persona === avatarId;
1320 const hasChatLock = chat_metadata['persona'] == avatarId;1320 const hasChatLock = chat_metadata.persona == avatarId;
13211321
1322 /** @type {PersonaConnection[]} */1322 /** @type {PersonaConnection[]} */
1323 const connections = power_user.persona_descriptions[avatarId]?.connections;1323 const connections = power_user.persona_descriptions[avatarId]?.connections;
@@ -1413,13 +1413,13 @@ function updatePersonaUIStates({ navigateToCurrent = false } = {}) {
1413 * @returns {PersonaLockInfo} An object containing flags and a message describing the persona lock status.1413 * @returns {PersonaLockInfo} An object containing flags and a message describing the persona lock status.
1414 */1414 */
1415function getPersonaTemporaryLockInfo() {1415function getPersonaTemporaryLockInfo() {
1416 const hasDifferentChatLock = !!chat_metadata['persona'] && chat_metadata['persona'] !== user_avatar;1416 const hasDifferentChatLock = !!chat_metadata.persona && chat_metadata.persona !== user_avatar;
1417 const hasDifferentDefaultLock = power_user.default_persona && power_user.default_persona !== user_avatar;1417 const hasDifferentDefaultLock = power_user.default_persona && power_user.default_persona !== user_avatar;
1418 const isTemporary = hasDifferentChatLock || (!chat_metadata['persona'] && hasDifferentDefaultLock);1418 const isTemporary = hasDifferentChatLock || (!chat_metadata.persona && hasDifferentDefaultLock);
1419 const info = isTemporary ? t`A different persona is locked to this chat, or you have a different default persona set. The currently selected persona will only be temporary, and resets on reload. Consider locking this persona to the chat if you want to permanently use it.`1419 const info = isTemporary ? t`A different persona is locked to this chat, or you have a different default persona set. The currently selected persona will only be temporary, and resets on reload. Consider locking this persona to the chat if you want to permanently use it.`
1420 + '\n\n'1420 + '\n\n'
1421 + t`Current Persona: ${power_user.personas[user_avatar]}`1421 + t`Current Persona: ${power_user.personas[user_avatar]}`
1422 + (hasDifferentChatLock ? '\n' + t`Chat persona: ${power_user.personas[chat_metadata['persona']]}` : '')1422 + (hasDifferentChatLock ? '\n' + t`Chat persona: ${power_user.personas[chat_metadata.persona]}` : '')
1423 + (hasDifferentDefaultLock ? '\n' + t`Default persona: ${power_user.personas[power_user.default_persona]}` : '') : '';1423 + (hasDifferentDefaultLock ? '\n' + t`Default persona: ${power_user.personas[power_user.default_persona]}` : '') : '';
14241424
1425 return {1425 return {
@@ -1458,14 +1458,14 @@ async function loadPersonaForCurrentChat({ doRender = false } = {}) {
1458 let connectType = null;1458 let connectType = null;
14591459
1460 // If persona is locked in chat metadata, select it1460 // If persona is locked in chat metadata, select it
1461 if (chat_metadata['persona']) {1461 if (chat_metadata.persona) {
1462 console.log(`Using locked persona ${chat_metadata['persona']}`);1462 console.log(`Using locked persona ${chat_metadata.persona}`);
1463 chatPersona = chat_metadata['persona'];1463 chatPersona = chat_metadata.persona;
14641464
1465 // Verify it exists1465 // Verify it exists
1466 if (!userAvatars.includes(chatPersona)) {1466 if (!userAvatars.includes(chatPersona)) {
1467 console.warn('Chat-locked persona avatar not found, unlocking persona');1467 console.warn('Chat-locked persona avatar not found, unlocking persona');
1468 delete chat_metadata['persona'];1468 delete chat_metadata.persona;
1469 saveSettingsDebounced();1469 saveSettingsDebounced();
1470 chatPersona = '';1470 chatPersona = '';
1471 }1471 }
@@ -1528,9 +1528,9 @@ async function loadPersonaForCurrentChat({ doRender = false } = {}) {
1528 }1528 }
15291529
1530 // Whatever way we selected a persona, if it doesn't exist, unlock this chat1530 // Whatever way we selected a persona, if it doesn't exist, unlock this chat
1531 if (chat_metadata['persona'] && !userAvatars.includes(chat_metadata['persona'])) {1531 if (chat_metadata.persona && !userAvatars.includes(chat_metadata.persona)) {
1532 console.warn('Persona avatar not found, unlocking persona');1532 console.warn('Persona avatar not found, unlocking persona');
1533 delete chat_metadata['persona'];1533 delete chat_metadata.persona;
1534 }1534 }
15351535
1536 // Default persona missing1536 // Default persona missing
@@ -1542,7 +1542,7 @@ async function loadPersonaForCurrentChat({ doRender = false } = {}) {
15421542
1543 // Persona avatar found, select it1543 // Persona avatar found, select it
1544 if (chatPersona && user_avatar !== chatPersona) {1544 if (chatPersona && user_avatar !== chatPersona) {
1545 const willAutoLock = power_user.persona_auto_lock && user_avatar !== chat_metadata['persona'];1545 const willAutoLock = power_user.persona_auto_lock && user_avatar !== chat_metadata.persona;
1546 await setUserAvatar(chatPersona, { toastPersonaNameChange: false, navigateToCurrent: true });1546 await setUserAvatar(chatPersona, { toastPersonaNameChange: false, navigateToCurrent: true });
15471547
1548 if (power_user.persona_show_notifications) {1548 if (power_user.persona_show_notifications) {
@@ -1554,7 +1554,7 @@ async function loadPersonaForCurrentChat({ doRender = false } = {}) {
1554 }1554 }
1555 }1555 }
1556 // Even if it's the same persona, we still might need to auto-lock to chat if that's enabled1556 // Even if it's the same persona, we still might need to auto-lock to chat if that's enabled
1557 else if (chatPersona && power_user.persona_auto_lock && !chat_metadata['persona']) {1557 else if (chatPersona && power_user.persona_auto_lock && !chat_metadata.persona) {
1558 await lockPersona('chat');1558 await lockPersona('chat');
1559 }1559 }
15601560
public/scripts/preset-manager.js+6 -6
@@ -163,7 +163,7 @@ class PresetManager {
163 const manager = getPresetManager('textgenerationwebui');163 const manager = getPresetManager('textgenerationwebui');
164 const name = manager.getSelectedPresetName();164 const name = manager.getSelectedPresetName();
165 const data = manager.getPresetSettings(name);165 const data = manager.getPresetSettings(name);
166 data['name'] = name;166 data.name = name;
167 return data;167 return data;
168 },168 },
169 setData: (data) => {169 setData: (data) => {
@@ -652,22 +652,22 @@ class PresetManager {
652 return textgen_settings;652 return textgen_settings;
653 case 'context': {653 case 'context': {
654 const context_preset = getContextSettings();654 const context_preset = getContextSettings();
655 context_preset['name'] = name || power_user.context.preset;655 context_preset.name = name || power_user.context.preset;
656 return context_preset;656 return context_preset;
657 }657 }
658 case 'instruct': {658 case 'instruct': {
659 const instruct_preset = structuredClone(power_user.instruct);659 const instruct_preset = structuredClone(power_user.instruct);
660 instruct_preset['name'] = name || power_user.instruct.preset;660 instruct_preset.name = name || power_user.instruct.preset;
661 return instruct_preset;661 return instruct_preset;
662 }662 }
663 case 'sysprompt': {663 case 'sysprompt': {
664 const sysprompt_preset = structuredClone(power_user.sysprompt);664 const sysprompt_preset = structuredClone(power_user.sysprompt);
665 sysprompt_preset['name'] = name || power_user.sysprompt.preset;665 sysprompt_preset.name = name || power_user.sysprompt.preset;
666 return sysprompt_preset;666 return sysprompt_preset;
667 }667 }
668 case 'reasoning': {668 case 'reasoning': {
669 const reasoning_preset = structuredClone(power_user.reasoning);669 const reasoning_preset = structuredClone(power_user.reasoning);
670 reasoning_preset['name'] = name || power_user.reasoning.preset;670 reasoning_preset.name = name || power_user.reasoning.preset;
671 return reasoning_preset;671 return reasoning_preset;
672 }672 }
673 default:673 default:
@@ -1115,7 +1115,7 @@ export async function initPresetManager() {
1115 const fileName = file.name.replace('.json', '').replace('.settings', '');1115 const fileName = file.name.replace('.json', '').replace('.settings', '');
1116 const data = await parseJsonFile(file);1116 const data = await parseJsonFile(file);
1117 const name = data?.name ?? fileName;1117 const name = data?.name ?? fileName;
1118 data['name'] = name;1118 data.name = name;
11191119
1120 await presetManager.savePreset(name, data);1120 await presetManager.savePreset(name, data);
1121 const successToast = !presetManager.isAdvancedFormatting() ? t`Preset imported` : t`Template imported`;1121 const successToast = !presetManager.isAdvancedFormatting() ? t`Preset imported` : t`Template imported`;
public/scripts/slash-commands.js+7 -7
@@ -2120,7 +2120,7 @@ export function initDefaultSlashCommands() {
2120 isRequired: true,2120 isRequired: true,
2121 enumProvider: (executor, scope) => [2121 enumProvider: (executor, scope) => [
2122 ...commonEnumProviders.variables('scope')(executor, scope),2122 ...commonEnumProviders.variables('scope')(executor, scope),
2123 ...(typeof window['qrEnumProviderExecutables'] === 'function') ? window['qrEnumProviderExecutables']() : [],2123 ...(typeof globalThis.qrEnumProviderExecutables === 'function') ? globalThis.qrEnumProviderExecutables() : [],
2124 ],2124 ],
2125 }),2125 }),
2126 ],2126 ],
@@ -3572,7 +3572,7 @@ async function runCallback(args, name) {
3572 return result.pipe;3572 return result.pipe;
3573 }3573 }
35743574
3575 if (typeof window['executeQuickReplyByName'] !== 'function') {3575 if (typeof globalThis.executeQuickReplyByName !== 'function') {
3576 throw new Error(t`Quick Reply extension is not loaded`);3576 throw new Error(t`Quick Reply extension is not loaded`);
3577 }3577 }
35783578
@@ -3583,7 +3583,7 @@ async function runCallback(args, name) {
3583 abortController: args._abortController,3583 abortController: args._abortController,
3584 debugController: args._debugController,3584 debugController: args._debugController,
3585 };3585 };
3586 return await window['executeQuickReplyByName'](name, args, options);3586 return await globalThis.executeQuickReplyByName(name, args, options);
3587 } catch (error) {3587 } catch (error) {
3588 throw new Error(t`Error running Quick Reply "${name}": ${error.message}`);3588 throw new Error(t`Error running Quick Reply "${name}": ${error.message}`);
3589 }3589 }
@@ -4788,7 +4788,7 @@ export async function sendMessageAs(args, text) {
4788 insertAt = chat.length + insertAt;4788 insertAt = chat.length + insertAt;
4789 }4789 }
47904790
4791 chat_metadata['tainted'] = true;4791 chat_metadata.tainted = true;
47924792
4793 if (!isNaN(insertAt) && insertAt >= 0 && insertAt <= chat.length) {4793 if (!isNaN(insertAt) && insertAt >= 0 && insertAt <= chat.length) {
4794 chat.splice(insertAt, 0, message);4794 chat.splice(insertAt, 0, message);
@@ -4840,7 +4840,7 @@ export async function sendNarratorMessage(args, text) {
4840 insertAt = chat.length + insertAt;4840 insertAt = chat.length + insertAt;
4841 }4841 }
48424842
4843 chat_metadata['tainted'] = true;4843 chat_metadata.tainted = true;
48444844
4845 if (!isNaN(insertAt) && insertAt >= 0 && insertAt <= chat.length) {4845 if (!isNaN(insertAt) && insertAt >= 0 && insertAt <= chat.length) {
4846 chat.splice(insertAt, 0, message);4846 chat.splice(insertAt, 0, message);
@@ -4892,7 +4892,7 @@ export async function promptQuietForLoudResponse(who, text) {
4892 },4892 },
4893 };4893 };
48944894
4895 chat_metadata['tainted'] = true;4895 chat_metadata.tainted = true;
48964896
4897 chat.push(message);4897 chat.push(message);
4898 await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1));4898 await eventSource.emit(event_types.MESSAGE_SENT, (chat.length - 1));
@@ -4928,7 +4928,7 @@ async function sendCommentMessage(args, text) {
4928 insertAt = chat.length + insertAt;4928 insertAt = chat.length + insertAt;
4929 }4929 }
49304930
4931 chat_metadata['tainted'] = true;4931 chat_metadata.tainted = true;
49324932
4933 if (!isNaN(insertAt) && insertAt >= 0 && insertAt <= chat.length) {4933 if (!isNaN(insertAt) && insertAt >= 0 && insertAt <= chat.length) {
4934 chat.splice(insertAt, 0, message);4934 chat.splice(insertAt, 0, message);
public/scripts/textgen-models.js+2 -2
@@ -880,8 +880,8 @@ async function downloadTabbyModel() {
880 }880 }
881881
882 // Params for the server side of ST882 // Params for the server side of ST
883 params['api_server'] = serverUrl;883 params.api_server = serverUrl;
884 params['api_type'] = textgen_settings.type;884 params.api_type = textgen_settings.type;
885885
886 toastr.info('Downloading. Check the Tabby console for progress reports.');886 toastr.info('Downloading. Check the Tabby console for progress reports.');
887887
public/scripts/textgen-settings.js+5 -5
@@ -755,7 +755,7 @@ async function getStatusTextgen() {
755 power_user.chat_template_hash = chat_template_hash;755 power_user.chat_template_hash = chat_template_hash;
756756
757 if (wantsContextSize && 'default_generation_settings' in data) {757 if (wantsContextSize && 'default_generation_settings' in data) {
758 const backend_max_context = data['default_generation_settings']['n_ctx'];758 const backend_max_context = data.default_generation_settings.n_ctx;
759 if (backend_max_context && typeof backend_max_context === 'number') {759 if (backend_max_context && typeof backend_max_context === 'number') {
760 const old_value = max_context;760 const old_value = max_context;
761 if (max_context !== backend_max_context) {761 if (max_context !== backend_max_context) {
@@ -913,16 +913,16 @@ export function initTextGenSettings() {
913 $('#ban_eos_token_textgenerationwebui').prop('checked', false); //Aphro should not ban EOS, just ignore it; 'add token '2' to ban list do to this'913 $('#ban_eos_token_textgenerationwebui').prop('checked', false); //Aphro should not ban EOS, just ignore it; 'add token '2' to ban list do to this'
914 //special handling for vLLM/Aphrodite topK -1 disable state914 //special handling for vLLM/Aphrodite topK -1 disable state
915 $('#top_k_textgenerationwebui').attr('min', -1);915 $('#top_k_textgenerationwebui').attr('min', -1);
916 if ($('#top_k_textgenerationwebui').val() === '0' || textgenerationwebui_settings['top_k'] === 0) {916 if ($('#top_k_textgenerationwebui').val() === '0' || textgenerationwebui_settings.top_k === 0) {
917 textgenerationwebui_settings['top_k'] = -1;917 textgenerationwebui_settings.top_k = -1;
918 $('#top_k_textgenerationwebui').val('-1').trigger('input');918 $('#top_k_textgenerationwebui').val('-1').trigger('input');
919 }919 }
920 } else {920 } else {
921 $('#mirostat_mode_textgenerationwebui').attr('step', 1);921 $('#mirostat_mode_textgenerationwebui').attr('step', 1);
922 //undo special vLLM/Aphrodite setup for topK922 //undo special vLLM/Aphrodite setup for topK
923 $('#top_k_textgenerationwebui').attr('min', 0);923 $('#top_k_textgenerationwebui').attr('min', 0);
924 if ($('#top_k_textgenerationwebui').val() === '-1' || textgenerationwebui_settings['top_k'] === -1) {924 if ($('#top_k_textgenerationwebui').val() === '-1' || textgenerationwebui_settings.top_k === -1) {
925 textgenerationwebui_settings['top_k'] = 0;925 textgenerationwebui_settings.top_k = 0;
926 $('#top_k_textgenerationwebui').val('0').trigger('input');926 $('#top_k_textgenerationwebui').val('0').trigger('input');
927 }927 }
928 }928 }
public/scripts/tool-calling.js+2 -2
@@ -410,8 +410,8 @@ export class ToolManager {
410 if (tools.length) {410 if (tools.length) {
411 console.log('[ToolManager] Registered function tools:', tools);411 console.log('[ToolManager] Registered function tools:', tools);
412412
413 data['tools'] = tools;413 data.tools = tools;
414 data['tool_choice'] = 'auto';414 data.tool_choice = 'auto';
415 }415 }
416 }416 }
417417
public/scripts/utils.js+10 -4
@@ -36,10 +36,16 @@ export const localizePagination = function (container) {
3636
37/**37/**
38 * Checks if the current environment supports negative lookbehind in regular expressions.38 * Checks if the current environment supports negative lookbehind in regular expressions.
39 * @type {{ (): boolean; result?: boolean }} Defines the function as a memoized object with a cached result.
39 * @returns {boolean} True if negative lookbehind is supported, false otherwise.40 * @returns {boolean} True if negative lookbehind is supported, false otherwise.
40 */41 */
41export function canUseNegativeLookbehind() {42export function canUseNegativeLookbehind() {
42 let result = canUseNegativeLookbehind['result'];43 /**
44 * A reference to the function itself, typed as a callable object with a cache property.
45 * @type {{ (): boolean; result?: boolean }}
46 */
47 const fn = canUseNegativeLookbehind;
48 let result = fn.result;
43 if (typeof result !== 'boolean') {49 if (typeof result !== 'boolean') {
44 try {50 try {
45 new RegExp('(?<!_)');51 new RegExp('(?<!_)');
@@ -47,7 +53,7 @@ export function canUseNegativeLookbehind() {
47 } catch (e) {53 } catch (e) {
48 result = false;54 result = false;
49 }55 }
50 canUseNegativeLookbehind['result'] = result;56 fn.result = result;
51 }57 }
52 return result;58 return result;
53}59}
@@ -2440,8 +2446,8 @@ export async function fetchFaFile(name) {
2440 const sheet = style.sheet;2446 const sheet = style.sheet;
2441 style.remove();2447 style.remove();
2442 return [...sheet.cssRules]2448 return [...sheet.cssRules]
2443 .filter(rule => rule.style?.content)2449 .filter(rule => rule['style']?.content)
2444 .map(rule => rule.selectorText.split(/,\s*/).map(selector => selector.split('::').shift().slice(1)))2450 .map(rule => rule['selectorText'].split(/,\s*/).map(selector => selector.split('::').shift().slice(1)))
2445 ;2451 ;
2446}2452}
24472453
public/scripts/world-info.js+1 -1
@@ -3271,7 +3271,7 @@ export async function getWorldEntry(name, data, entry) {
3271 const commentInput = headerTemplate.find('textarea[name="comment"]');3271 const commentInput = headerTemplate.find('textarea[name="comment"]');
32723272
3273 //Update the commentInput's placeholder.3273 //Update the commentInput's placeholder.
3274 const keys = entry['key'].join(', ');3274 const keys = entry.key.join(', ');
3275 setCommentPlaceholder(keys, commentInput);3275 setCommentPlaceholder(keys, commentInput);
32763276
3277 commentInput.data('uid', entry.uid);3277 commentInput.data('uid', entry.uid);
src/endpoints/assets.js+2 -2
@@ -145,7 +145,7 @@ router.post('/get', async (request, response) => {
145 for (let file of files) {145 for (let file of files) {
146 if (!file.endsWith('.placeholder')) {146 if (!file.endsWith('.placeholder')) {
147 //console.debug("Asset VRM model found:",file)147 //console.debug("Asset VRM model found:",file)
148 output['vrm']['model'].push(clientRelativePath(request.user.directories.root, file));148 output.vrm.model.push(clientRelativePath(request.user.directories.root, file));
149 }149 }
150 }150 }
151151
@@ -156,7 +156,7 @@ router.post('/get', async (request, response) => {
156 for (let file of files) {156 for (let file of files) {
157 if (!file.endsWith('.placeholder')) {157 if (!file.endsWith('.placeholder')) {
158 //console.debug("Asset VRM animation found:",file)158 //console.debug("Asset VRM animation found:",file)
159 output['vrm']['animation'].push(clientRelativePath(request.user.directories.root, file));159 output.vrm.animation.push(clientRelativePath(request.user.directories.root, file));
160 }160 }
161 }161 }
162 continue;162 continue;
src/endpoints/backends/chat-completions.js+2 -2
@@ -245,7 +245,7 @@ async function sendClaudeRequest(request, response) {
245 };245 };
246 if (useSystemPrompt) {246 if (useSystemPrompt) {
247 if (enableSystemPromptCache && Array.isArray(convertedPrompt.systemPrompt) && convertedPrompt.systemPrompt.length) {247 if (enableSystemPromptCache && Array.isArray(convertedPrompt.systemPrompt) && convertedPrompt.systemPrompt.length) {
248 convertedPrompt.systemPrompt[convertedPrompt.systemPrompt.length - 1]['cache_control'] = { type: 'ephemeral', ttl: cacheTTL };248 convertedPrompt.systemPrompt[convertedPrompt.systemPrompt.length - 1].cache_control = { type: 'ephemeral', ttl: cacheTTL };
249 }249 }
250250
251 requestBody.system = convertedPrompt.systemPrompt;251 requestBody.system = convertedPrompt.systemPrompt;
@@ -261,7 +261,7 @@ async function sendClaudeRequest(request, response) {
261 .map(fn => ({ name: fn.name, description: fn.description, input_schema: flattenSchema(fn.parameters, request.body.chat_completion_source) }));261 .map(fn => ({ name: fn.name, description: fn.description, input_schema: flattenSchema(fn.parameters, request.body.chat_completion_source) }));
262262
263 if (enableSystemPromptCache && requestBody.tools.length) {263 if (enableSystemPromptCache && requestBody.tools.length) {
264 requestBody.tools[requestBody.tools.length - 1]['cache_control'] = { type: 'ephemeral', ttl: cacheTTL };264 requestBody.tools[requestBody.tools.length - 1].cache_control = { type: 'ephemeral', ttl: cacheTTL };
265 }265 }
266 }266 }
267267
src/endpoints/backends/kobold.js+1 -1
@@ -76,7 +76,7 @@ router.post('/generate', async function (request, response_generate) {
76 sampler_seed: request.body.sampler_seed,76 sampler_seed: request.body.sampler_seed,
77 };77 };
78 if (request.body.stop_sequence) {78 if (request.body.stop_sequence) {
79 this_settings['stop_sequence'] = request.body.stop_sequence;79 this_settings.stop_sequence = request.body.stop_sequence;
80 }80 }
81 }81 }
8282
src/endpoints/backends/text-completions.js+6 -6
@@ -257,10 +257,10 @@ router.post('/props', async function (request, response) {
257 /** @type {any} */257 /** @type {any} */
258 const props = await propsReply.json();258 const props = await propsReply.json();
259 // TEMPORARY: llama.cpp's /props endpoint has a bug which replaces the last newline with a \0259 // TEMPORARY: llama.cpp's /props endpoint has a bug which replaces the last newline with a \0
260 if (apiType === TEXTGEN_TYPES.LLAMACPP && props['chat_template'] && props['chat_template'].endsWith('\u0000')) {260 if (apiType === TEXTGEN_TYPES.LLAMACPP && props.chat_template && props.chat_template.endsWith('\u0000')) {
261 props['chat_template'] = props['chat_template'].slice(0, -1) + '\n';261 props.chat_template = props.chat_template.slice(0, -1) + '\n';
262 }262 }
263 props['chat_template_hash'] = createHash('sha256').update(props['chat_template']).digest('hex');263 props.chat_template_hash = createHash('sha256').update(props.chat_template).digest('hex');
264 console.debug(`Model properties: ${JSON.stringify(props)}`);264 console.debug(`Model properties: ${JSON.stringify(props)}`);
265 return response.send(props);265 return response.send(props);
266 } catch (error) {266 } catch (error) {
@@ -380,7 +380,7 @@ router.post('/generate', async function (request, response) {
380 const keepAlive = Number(getConfigValue('ollama.keepAlive', -1, 'number'));380 const keepAlive = Number(getConfigValue('ollama.keepAlive', -1, 'number'));
381 const numBatch = Number(getConfigValue('ollama.batchSize', -1, 'number'));381 const numBatch = Number(getConfigValue('ollama.batchSize', -1, 'number'));
382 if (numBatch > 0) {382 if (numBatch > 0) {
383 request.body['num_batch'] = numBatch;383 request.body.num_batch = numBatch;
384 }384 }
385 args.body = JSON.stringify({385 args.body = JSON.stringify({
386 model: request.body.model,386 model: request.body.model,
@@ -410,7 +410,7 @@ router.post('/generate', async function (request, response) {
410410
411 // Map InfermaticAI response to OAI completions format411 // Map InfermaticAI response to OAI completions format
412 if (apiType === TEXTGEN_TYPES.INFERMATICAI) {412 if (apiType === TEXTGEN_TYPES.INFERMATICAI) {
413 data['choices'] = (data?.choices || []).map(choice => ({ text: choice?.message?.content || choice.text, logprobs: choice?.logprobs, index: choice?.index }));413 data.choices = (data?.choices || []).map(choice => ({ text: choice?.message?.content || choice.text, logprobs: choice?.logprobs, index: choice?.index }));
414 }414 }
415415
416 return response.send(data);416 return response.send(data);
@@ -616,7 +616,7 @@ tabby.post('/download', async function (request, response) {
616 /** @type {any} */616 /** @type {any} */
617 const permissionJson = await permissionResponse.json();617 const permissionJson = await permissionResponse.json();
618618
619 if (permissionJson['permission'] !== 'admin') {619 if (permissionJson.permission !== 'admin') {
620 return response.status(403).send({ error: true });620 return response.status(403).send({ error: true });
621 }621 }
622 } else {622 } else {
src/endpoints/characters.js+13 -13
@@ -412,16 +412,16 @@ const processCharacter = async (item, directories, { shallow }) => {
412 let jsonObject = getCharaCardV2(JSON.parse(imgData), directories, false);412 let jsonObject = getCharaCardV2(JSON.parse(imgData), directories, false);
413 jsonObject.avatar = item;413 jsonObject.avatar = item;
414 const character = jsonObject;414 const character = jsonObject;
415 character['json_data'] = imgData;415 character.json_data = imgData;
416 const charStat = fs.statSync(path.join(directories.characters, item));416 const charStat = fs.statSync(path.join(directories.characters, item));
417 character['date_added'] = charStat.ctimeMs;417 character.date_added = charStat.ctimeMs;
418 character['create_date'] = jsonObject['create_date'] || new Date(Math.round(charStat.ctimeMs)).toISOString();418 character.create_date = jsonObject.create_date || new Date(Math.round(charStat.ctimeMs)).toISOString();
419 const chatsDirectory = path.join(directories.chats, item.replace('.png', ''));419 const chatsDirectory = path.join(directories.chats, item.replace('.png', ''));
420420
421 const { chatSize, dateLastChat } = calculateChatSize(chatsDirectory);421 const { chatSize, dateLastChat } = calculateChatSize(chatsDirectory);
422 character['chat_size'] = chatSize;422 character.chat_size = chatSize;
423 character['date_last_chat'] = dateLastChat;423 character.date_last_chat = dateLastChat;
424 character['data_size'] = calculateDataSize(jsonObject?.data);424 character.data_size = calculateDataSize(jsonObject?.data);
425 return shallow ? toShallow(character) : character;425 return shallow ? toShallow(character) : character;
426 }426 }
427 catch (err) {427 catch (err) {
@@ -504,7 +504,7 @@ function unsetPrivateFields(char) {
504504
505function readFromV2(char) {505function readFromV2(char) {
506 if (_.isUndefined(char.data)) {506 if (_.isUndefined(char.data)) {
507 console.warn(`Char ${char['name']} has Spec v2 data missing`);507 console.warn(`Char ${char.name} has Spec v2 data missing`);
508 return char;508 return char;
509 }509 }
510510
@@ -542,17 +542,17 @@ function readFromV2(char) {
542 //console.warn(`Spec v2 extension data missing for field: ${charField}, using default value: ${defaultValue}`);542 //console.warn(`Spec v2 extension data missing for field: ${charField}, using default value: ${defaultValue}`);
543 char[charField] = defaultValue;543 char[charField] = defaultValue;
544 } else {544 } else {
545 console.warn(`Char ${char['name']} has Spec v2 data missing for unknown field: ${charField}`);545 console.warn(`Char ${char.name} has Spec v2 data missing for unknown field: ${charField}`);
546 return;546 return;
547 }547 }
548 }548 }
549 if (!_.isUndefined(char[charField]) && !_.isUndefined(v2Value) && String(char[charField]) !== String(v2Value)) {549 if (!_.isUndefined(char[charField]) && !_.isUndefined(v2Value) && String(char[charField]) !== String(v2Value)) {
550 console.warn(`Char ${char['name']} has Spec v2 data mismatch with Spec v1 for field: ${charField}`, char[charField], v2Value);550 console.warn(`Char ${char.name} has Spec v2 data mismatch with Spec v1 for field: ${charField}`, char[charField], v2Value);
551 }551 }
552 char[charField] = v2Value;552 char[charField] = v2Value;
553 });553 });
554554
555 char['chat'] = char['chat'] ?? `${char.name} - ${humanizedDateTime()}`;555 char.chat = char.chat ?? `${char.name} - ${humanizedDateTime()}`;
556556
557 return char;557 return char;
558}558}
@@ -779,7 +779,7 @@ async function importFromCharX(uploadPath, { request }, preservedFileName) {
779 // Apply standard character transformations779 // Apply standard character transformations
780 let processedCard = readFromV2(card);780 let processedCard = readFromV2(card);
781 unsetPrivateFields(processedCard);781 unsetPrivateFields(processedCard);
782 processedCard['create_date'] = new Date().toISOString();782 processedCard.create_date = new Date().toISOString();
783 processedCard.name = sanitize(processedCard.name);783 processedCard.name = sanitize(processedCard.name);
784784
785 const fileName = preservedFileName || getPngName(processedCard.name, request.user.directories);785 const fileName = preservedFileName || getPngName(processedCard.name, request.user.directories);
@@ -893,7 +893,7 @@ async function importFromJson(uploadPath, { request }, preservedFileName) {
893 importRisuSprites(request.user.directories, jsonData);893 importRisuSprites(request.user.directories, jsonData);
894 unsetPrivateFields(jsonData);894 unsetPrivateFields(jsonData);
895 jsonData = readFromV2(jsonData);895 jsonData = readFromV2(jsonData);
896 jsonData['create_date'] = new Date().toISOString();896 jsonData.create_date = new Date().toISOString();
897 const pngName = preservedFileName || getPngName(jsonData.data?.name || jsonData.name, request.user.directories);897 const pngName = preservedFileName || getPngName(jsonData.data?.name || jsonData.name, request.user.directories);
898 const char = JSON.stringify(jsonData);898 const char = JSON.stringify(jsonData);
899 const result = await writeCharacterData(DEFAULT_AVATAR_PATH, char, pngName, request);899 const result = await writeCharacterData(DEFAULT_AVATAR_PATH, char, pngName, request);
@@ -976,7 +976,7 @@ async function importFromPng(uploadPath, { request }, preservedFileName) {
976 importRisuSprites(request.user.directories, jsonData);976 importRisuSprites(request.user.directories, jsonData);
977 unsetPrivateFields(jsonData);977 unsetPrivateFields(jsonData);
978 jsonData = readFromV2(jsonData);978 jsonData = readFromV2(jsonData);
979 jsonData['create_date'] = new Date().toISOString();979 jsonData.create_date = new Date().toISOString();
980 const char = JSON.stringify(jsonData);980 const char = JSON.stringify(jsonData);
981 const result = await writeCharacterData(uploadPath, char, pngName, request);981 const result = await writeCharacterData(uploadPath, char, pngName, request);
982 fs.unlinkSync(uploadPath);982 fs.unlinkSync(uploadPath);
src/endpoints/chats.js+2 -2
@@ -397,8 +397,8 @@ export async function getChatInfo(pathToFile, additionalData = {}, withMetadata
397 const jsonData = tryParse(lastLine);397 const jsonData = tryParse(lastLine);
398 if (jsonData && (jsonData.name || jsonData.character_name || jsonData.chat_metadata)) {398 if (jsonData && (jsonData.name || jsonData.character_name || jsonData.chat_metadata)) {
399 chatData.chat_items = (itemCounter - 1);399 chatData.chat_items = (itemCounter - 1);
400 chatData.mes = jsonData['mes'] || '[The message is empty]';400 chatData.mes = jsonData.mes || '[The message is empty]';
401 chatData.last_mes = jsonData['send_date'] || new Date(Math.round(stats.mtimeMs)).toISOString();401 chatData.last_mes = jsonData.send_date || new Date(Math.round(stats.mtimeMs)).toISOString();
402402
403 res(chatData);403 res(chatData);
404 } else {404 } else {
src/endpoints/groups.js+4 -4
@@ -126,8 +126,8 @@ router.post('/all', (request, response) => {
126 const fileContents = fs.readFileSync(filePath, 'utf8');126 const fileContents = fs.readFileSync(filePath, 'utf8');
127 const group = JSON.parse(fileContents);127 const group = JSON.parse(fileContents);
128 const groupStat = fs.statSync(filePath);128 const groupStat = fs.statSync(filePath);
129 group['date_added'] = groupStat.birthtimeMs;129 group.date_added = groupStat.birthtimeMs;
130 group['create_date'] = new Date(groupStat.birthtimeMs).toISOString();130 group.create_date = new Date(groupStat.birthtimeMs).toISOString();
131131
132 let chat_size = 0;132 let chat_size = 0;
133 let date_last_chat = 0;133 let date_last_chat = 0;
@@ -142,8 +142,8 @@ router.post('/all', (request, response) => {
142 }142 }
143 }143 }
144144
145 group['date_last_chat'] = date_last_chat;145 group.date_last_chat = date_last_chat;
146 group['chat_size'] = chat_size;146 group.chat_size = chat_size;
147 groups.push(group);147 groups.push(group);
148 }148 }
149 catch (error) {149 catch (error) {
src/endpoints/stable-diffusion.js+5 -5
@@ -228,7 +228,7 @@ router.post('/get-model', async (request, response) => {
228 });228 });
229 /** @type {any} */229 /** @type {any} */
230 const data = await result.json();230 const data = await result.json();
231 return response.send(data['sd_model_checkpoint']);231 return response.send(data.sd_model_checkpoint);
232 } catch (error) {232 } catch (error) {
233 console.error(error);233 console.error(error);
234 return response.sendStatus(500);234 return response.sendStatus(500);
@@ -277,8 +277,8 @@ router.post('/set-model', async (request, response) => {
277 /** @type {any} */277 /** @type {any} */
278 const progressState = await getProgress();278 const progressState = await getProgress();
279279
280 const progress = progressState['progress'];280 const progress = progressState.progress;
281 const jobCount = progressState['state']['job_count'];281 const jobCount = progressState.state.job_count;
282 if (progress === 0.0 && jobCount === 0) {282 if (progress === 0.0 && jobCount === 0) {
283 break;283 break;
284 }284 }
@@ -834,7 +834,7 @@ drawthings.post('/get-model', async (request, response) => {
834 /** @type {any} */834 /** @type {any} */
835 const data = await result.json();835 const data = await result.json();
836836
837 return response.send(data['model']);837 return response.send(data.model);
838 } catch (error) {838 } catch (error) {
839 console.error(error);839 console.error(error);
840 return response.sendStatus(500);840 return response.sendStatus(500);
@@ -853,7 +853,7 @@ drawthings.post('/get-upscaler', async (request, response) => {
853 /** @type {any} */853 /** @type {any} */
854 const data = await result.json();854 const data = await result.json();
855855
856 return response.send(data['upscaler']);856 return response.send(data.upscaler);
857 } catch (error) {857 } catch (error) {
858 console.error(error);858 console.error(error);
859 return response.sendStatus(500);859 return response.sendStatus(500);
src/endpoints/tokenizers.js+2 -2
@@ -1052,8 +1052,8 @@ router.post('/remote/kobold/count', async function (request, response) {
10521052
1053 /** @type {any} */1053 /** @type {any} */
1054 const data = await result.json();1054 const data = await result.json();
1055 const count = data['value'];1055 const count = data.value;
1056 const ids = data['ids'] ?? [];1056 const ids = data.ids ?? [];
1057 return response.send({ count, ids });1057 return response.send({ count, ids });
1058 } catch (error) {1058 } catch (error) {
1059 console.error(error);1059 console.error(error);
tests/frontend/MacroEngine.e2e.js+3 -3
@@ -639,8 +639,8 @@ test.describe('MacroEngine', () => {
639 await page.evaluate(async ([originalHash]) => {639 await page.evaluate(async ([originalHash]) => {
640 /** @type {import('../../public/script.js')} */640 /** @type {import('../../public/script.js')} */
641 const { chat_metadata } = await import('./script.js');641 const { chat_metadata } = await import('./script.js');
642 originalHash = chat_metadata['chat_id_hash'];642 originalHash = chat_metadata.chat_id_hash;
643 chat_metadata['chat_id_hash'] = 123456;643 chat_metadata.chat_id_hash = 123456;
644 }, [originalHash]);644 }, [originalHash]);
645645
646 const input = 'Choices: {{pick::red::green::blue}}, {{pick::red::green::blue}}.';646 const input = 'Choices: {{pick::red::green::blue}}, {{pick::red::green::blue}}.';
@@ -668,7 +668,7 @@ test.describe('MacroEngine', () => {
668 await page.evaluate(async ([originalHash]) => {668 await page.evaluate(async ([originalHash]) => {
669 /** @type {import('../../public/script.js')} */669 /** @type {import('../../public/script.js')} */
670 const { chat_metadata } = await import('./script.js');670 const { chat_metadata } = await import('./script.js');
671 chat_metadata['chat_id_hash'] = originalHash;671 chat_metadata.chat_id_hash = originalHash;
672 }, [originalHash]);672 }, [originalHash]);
673 });673 });
674 });674 });