Refactor: Replace Deprecated Code (#4221) * refactor: :recycle: Replace deprecated code defined within this repository - Replace ““/public/scripts.js” > `callPopup`” with ““/public/scripts/popup.js” > `callGenericPopup`”. - Replace ““/public/scripts/tokenizers.js” > `getTokenCount`” with ““/public/scripts/tokenizers.js” > `getTokenCountAsync`”. - Replace ““/public/scripts/extensions/assets/index.js” > `executeSlashCommands`” with ““/public/scripts/extensions/assets/index.js” > `executeSlashCommandsWithOptions`”. * refactor: :recycle: Replace deprecated code from standard library 1. Replace set of deprecated `escape` and deprecated `unescape` functions with `encodeURIComponent` and `decodeURIComponent` functions. 2. Replace set of `encodeURIComponent` and deprecated `unescape` functions with a custom `convertTextToBinaryString` function. * refactor: :recycle: Replace deprecated code imported from external library All of them is from jQuery. 1. Replace `$(document).ready(() => { ... })` → `jQuery(() => { ... })`. 2. Replace event type direct calls. 1) Change - `$('...').change(function () { ... })` → `$('...').on('change', function () { ... })`. 2) Click - `$('...').click(function () { ... })` → `$('...').on('click', function () { ... })`. - `$('...').click()` → `$('...').trigger('click')`. 3) Focus - `$('...').focus(function () { ... })` → `$('...').on('focus', function () { ... })`. - `$('...').focus()` → `$('...').trigger('focus')`. 4) Blur - `$('...').blur(function () { ... })` → `$('...').on('blur', function () { ... })`. 5) Keyup - `$('...').keyup(function () { ... })` → `$('...').on('keyup', function () { ... })`. * refactor(Attachment): :recycle: Merge `convertTextToBinaryString` and `convertTextToBase64` `convertTextToBase64` function use `Uint8Array.prototype.toBase64` if supported by the Browser and use a combination of `Window.btoa` and `String.fromCharCode` as fallback. * fix: :bug: Fix Bind World Info to Character Card Using the new `Popup` class. * fix: :bug: Fix Bind World Info to Character Card Using the new `Popup` class. * refactor: 🐛 Refactor and fix Character World Info popup Add singleton back and some optimization. - **🛡️ Singleton Pattern:** Implemented a singleton pattern using the `characterWorldPopup` variable. This prevents multiple instances of the popup from being created, ensuring a clean and predictable UI. If the popup is already open, it will be brought into focus instead of creating a duplicate. - **♻️ Improved Readability:** The event handling logic has been extracted into clearly named functions (`handlePrimaryWorldSelect`, `handleExtrasWorldSelect`), making the `Popup` constructor cleaner and the overall code more readable and maintainable. - **⚙️ Optimized Performance:** The new implementation caches frequently accessed values and jQuery selectors, reducing redundant DOM lookups and improving performance. --- * refactor(Chat): ♻️ Modernize chat deletion confirmation Replaces the deprecated `callPopup` with the modern `callGenericPopup` for the chat deletion confirmation dialog. * Fix /delchat command * Remove unused code * Fix type. Change value to debug log * Remove value logs * Remove singleton pattern for openCharacterWorldPopup --------- Co-authored-by: Cohee <18619528+Cohee1207@users.noreply.github.com>

1547a53d7c7de5c9225f33cf5d78486e75bc2623

GregW6 <atlanticgreg1@gmail.com>

Signed
12 files changed, +197 -165Ignore whitespace
public/script.js+135 -112
@@ -3560,7 +3560,7 @@ class StreamingProcessor {
35603560
35613561 // Token count update.
35623562 const tokenCountText = this.reasoningHandler.reasoning + processedText;
35633563 const currentTokenCount = isFinal && power_user.message_token_count_enabled ? getTokenCountawait getTokenCountAsync(tokenCountText, 0) : 0;
35643564 if (currentTokenCount) {
35653565 chat[messageId]['extra']['token_count'] = currentTokenCount;
35663566 if (this.messageTokenCounterDom instanceof HTMLElement) {
@@ -8930,19 +8930,25 @@ function updateAlternateGreetingsHintVisibility(root) {
89308930 $(root).find('.alternate_grettings_hint').toggle(numberOfGreetings == 0);
89318931}
89328932
89338933async function openCharacterWorldPopup() {
89348934 const chid = $('#set_character_world').data('chid');
8935-
89368935 if (menu_type != 'create' && chid === undefined) {
89378936 toastr.error('Does not have an Id for this character in world select menu.');
89388937 return;
89398938 }
89408939
8941- async function onSelectCharacterWorld() {
8940+ // TODO: Maybe make this utility function not use the window context?
8942- const value = $('.character_world_info_selector').find('option:selected').val();
8941+ const fileName = getCharaFilename(chid);
89438942 const worldIndexcharName = value(menu_type !== 'create' ? Number(value)create_save.name : NaNcharacters[chid]?.data?.name) || 'Nameless';
8944- const name = !isNaN(worldIndex) ? world_names[worldIndex] : '';
8943+ const worldId = (menu_type == 'create' ? create_save.world : characters[chid]?.data?.extensions?.world) || '';
8944+ const template = $('#character_world_template .character_world').clone();
8945+ template.find('.character_name').text(charName);
89458946
8947+ // --- Event Handlers ---
8948+ async function handlePrimaryWorldSelect() {
8949+ const selectedValue = $(this).val();
8950+ const worldIndex = selectedValue !== '' ? Number(selectedValue) : NaN;
8951+ const name = !isNaN(worldIndex) ? world_names[worldIndex] : '';
89468952 const previousValue = $('#character_world').val();
89478953 $('#character_world').val(name);
89488954
@@ -8970,26 +8976,21 @@ function openCharacterWorldPopup() {
89708976 await createOrEditCharacter();
89718977 }
89728978
89738979 setWorldInfoButtonClass(undefined, !!valuename);
89748980 }
89758981
89768982 function onExtraWorldInfoChangedhandleExtrasWorldSelect() {
89778983 const selectorFieldValueselectedValues = $('.character_extra_world_info_selector'this).val();
89788984 const selectedWorlds = Array.isArray(selectorFieldValueselectedValues) ? selectorFieldValueselectedValues : [];
89798985 let charLore = world_info.charLore ?? [];
8980-
8986+ const tempExtraBooks = selectedWorlds.map((index) => world_names[index]).filter(Boolean);
8981- // TODO: Maybe make this utility function not use the window context?
8982- const fileName = getCharaFilename(chid);
8983- const tempExtraBooks = selectedWorlds.map((index) => world_names[index]).filter((e) => e !== undefined);
8984-
89858987 const existingCharIndex = charLore.findIndex((e) => e.name === fileName);
8986- if (existingCharIndex === -1) {
8987- const newCharLoreEntry = {
8988- name: fileName,
8989- extraBooks: tempExtraBooks,
8990- };
89918988
8992- charLore.push(newCharLoreEntry);
8989+ if (existingCharIndex === -1) {
8990+ // Add record only if at least 1 lorebook is selected.
8991+ if (tempExtraBooks.length > 0) {
8992+ charLore.push({ name: fileName, extraBooks: tempExtraBooks });
8993+ }
89938994 } else if (tempExtraBooks.length === 0) {
89948995 charLore.splice(existingCharIndex, 1);
89958996 } else {
@@ -9000,62 +9001,42 @@ function openCharacterWorldPopup() {
90009001 saveSettingsDebounced();
90019002 }
90029003
9003- const template = $('#character_world_template .character_world').clone();
9004+ // --- Populate Dropdowns ---
9004- const select = template.find('.character_world_info_selector');
9005+ // Append to primary dropdown.
90059006 const extraSelectprimarySelect = template.find('.character_extra_world_info_selectorcharacter_world_info_selector');
9006- const name = (menu_type == 'create' ? create_save.name : characters[chid]?.data?.name) || 'Nameless';
9007- const worldId = (menu_type == 'create' ? create_save.world : characters[chid]?.data?.extensions?.world) || '';
9008- template.find('.character_name').text(name);
9009-
9010- // Not needed on mobile
9011- if (!isMobile()) {
9012- $(extraSelect).select2({
9013- width: '100%',
9014- placeholder: t`No auxillary Lorebooks set. Click here to select.`,
9015- allowClear: true,
9016- closeOnSelect: false,
9017- });
9018- }
9019-
9020- // Apped to base dropdown
90219007 world_names.forEach((item, i) => {
9022- const option = document.createElement('option');
9008+ primarySelect.append(new Option(item, String(i), item === worldId, item === worldId));
9023- option.value = String(i);
9024- option.innerText = item;
9025- option.selected = item === worldId;
9026- select.append(option);
90279009 });
90289010
90299011 // Append to extras dropdown.
9030- if (world_names.length > 0) {
9012+ const extrasSelect = template.find('.character_extra_world_info_selector');
9031- extraSelect.empty();
9013+ const existingCharLore = world_info.charLore?.find((e) => e.name === fileName);
9032- }
90339014 world_names.forEach((item, i) => {
90349015 const optionisSelected = document!!existingCharLore?.createElementextraBooks.includes('option'item);
9035- option.value = String(i);
9016+ extrasSelect.append(new Option(item, String(i), isSelected, isSelected));
9036- option.innerText = item;
9037-
9038- const existingCharLore = world_info.charLore?.find((e) => e.name === getCharaFilename());
9039- if (existingCharLore) {
9040- option.selected = existingCharLore.extraBooks.includes(item);
9041- } else {
9042- option.selected = false;
9043- }
9044- extraSelect.append(option);
90459017 });
90469018
9047- select.on('change', onSelectCharacterWorld);
9019+ const popup = new Popup(template, POPUP_TYPE.TEXT, '', {
9048- extraSelect.on('mousedown change', async function (e) {
9020+ onOpen: function (popup) {
9049- // If there's no world names, don't do anything
9021+ const popupDialog = $(popup.dlg);
9050- if (world_names.length === 0) {
9022+
90519023 eprimarySelect.preventDefaulton('change', handlePrimaryWorldSelect);
9052- return;
9024+ extrasSelect.on('change', handleExtrasWorldSelect);
9053- }
9025+
9054-
9026+ // Not needed on mobile.
9055- onExtraWorldInfoChanged();
9027+ if (!isMobile()) {
9028+ extrasSelect.select2({
9029+ width: '100%',
9030+ placeholder: t`No auxiliary Lorebooks set. Click here to select.`,
9031+ allowClear: true,
9032+ closeOnSelect: false,
9033+ dropdownParent: popupDialog,
9034+ });
9035+ }
9036+ },
90569037 });
90579038
9058- callPopup(template, 'text');
9039+ await popup.show();
90599040}
90609041
90619042function openAlternateGreetings() {
@@ -10089,12 +10070,32 @@ export async function doNewChat({ deleteCurrentChat = false } = {}) {
1008910070}
1009010071
1009110072async function doDeleteChat() {
10092- await displayPastChats();
10073+ return displayPastChats().then(() => new Promise((resolve) => {
10093- let currentChatDeleteButton = $('.select_chat_block[highlight=\'true\']').parent().find('.PastChat_cross');
10074+ let resolved = false;
10094- $(currentChatDeleteButton).trigger('click');
10075+ const timeOutId = setTimeout(() => {
10095- await delay(1);
10076+ toastr.error(t`Chat deletion timed out. Please try again.`);
10096- $('#dialogue_popup_ok').trigger('click', { fromSlashCommand: true });
10077+ setResolved();
10097- return '';
10078+ }, 5000);
10079+
10080+ const setResolved = () => {
10081+ if (resolved) {
10082+ return;
10083+ }
10084+ resolved = true;
10085+ [event_types.CHAT_DELETED, event_types.GROUP_CHAT_DELETED].forEach((eventType) => {
10086+ eventSource.removeListener(eventType, setResolved);
10087+ });
10088+ clearTimeout(timeOutId);
10089+ resolve('');
10090+ };
10091+
10092+ [event_types.CHAT_DELETED, event_types.GROUP_CHAT_DELETED].forEach((eventType) => {
10093+ eventSource.on(eventType, setResolved);
10094+ });
10095+
10096+ const currentChatDeleteButton = $('.select_chat_block[highlight=\'true\']').parent().find('.PastChat_cross');
10097+ $(currentChatDeleteButton).trigger('click', { fromSlashCommand: true });
10098+ }));
1009810099}
1009910100
1010010101async function doRenameChat(_, chatName) {
@@ -10764,10 +10765,10 @@ jQuery(async function () {
1076410765 });
1076510766 $('#send_but, #option_regenerate, #option_continue, #mes_continue, #mes_impersonate').on('click', () => {
1076610767 if (S_TAPreviouslyFocused) {
1076710768 $('#send_textarea').focustrigger('focus');
1076810769 }
1076910770 });
1077010771 $(document).clickon('click', event => {
1077110772 if ($(':focus').attr('id') !== 'send_textarea') {
1077210773 var validIDs = ['options_button', 'send_but', 'mes_impersonate', 'mes_continue', 'send_textarea', 'option_regenerate', 'option_continue'];
1077310774 if (!validIDs.includes($(event.target).attr('id'))) {
@@ -10780,7 +10781,7 @@ jQuery(async function () {
1078010781
1078110782 /////////////////
1078210783
1078310784 $('#swipes-checkbox').changeon('change', function () {
1078410785 swipes = !!$('#swipes-checkbox').prop('checked');
1078510786 if (swipes) {
1078610787 //console.log('toggle change calling showswipebtns');
@@ -10923,11 +10924,51 @@ jQuery(async function () {
1092310924 }
1092410925 });
1092510926
10926- $(document).on('click', '.PastChat_cross', function (e) {
10927+ /**
10928+ * Handles the deletion of a chat file, including group chats.
10929+ *
10930+ * @param {string} chatFile - The name of the chat file to delete.
10931+ * @param {object} group - The group object if the chat is part of a group.
10932+ * @param {boolean} [fromSlashCommand=false] - Whether the deletion was triggered from a slash command.
10933+ * @returns {Promise<void>}
10934+ */
10935+ async function handleDeleteChat(chatFile, group, fromSlashCommand = false) {
10936+ // Close past chat popup.
10937+ $('#select_chat_cross').trigger('click');
10938+ showLoader();
10939+ if (group) {
10940+ await deleteGroupChat(group, chatFile);
10941+ } else {
10942+ await delChat(chatFile);
10943+ }
10944+
10945+ if (fromSlashCommand) { // When called from `/delchat` command, don't re-open the history view.
10946+ $('#options').hide(); // Hide option popup menu.
10947+ hideLoader();
10948+ } else { // Open the history view again after 2 seconds (delay to avoid edge cases for deleting last chat).
10949+ setTimeout(function () {
10950+ $('#option_select_chat').trigger('click');
10951+ $('#options').hide(); // Hide option popup menu.
10952+ hideLoader();
10953+ }, 2000);
10954+ }
10955+ }
10956+
10957+ $(document).on('click', '.PastChat_cross', async function (e, { fromSlashCommand = false } = {}) {
1092710958 e.stopPropagation();
1092810959 chat_file_for_del = $(this).attr('file_name');
1092910960 console.debug('detected cross click for' + chat_file_for_del);
10930- callPopup('<h3>' + t`Delete the Chat File?` + '</h3>', 'del_chat');
10961+
10962+ // Skip confirmation if called from a slash command.
10963+ if (fromSlashCommand) {
10964+ await handleDeleteChat(chat_file_for_del, selected_group, true);
10965+ return;
10966+ }
10967+
10968+ const result = await callGenericPopup('<h3>' + t`Delete the Chat File?` + '</h3>', POPUP_TYPE.CONFIRM);
10969+ if (result === POPUP_RESULT.AFFIRMATIVE) {
10970+ await handleDeleteChat(chat_file_for_del, selected_group, false);
10971+ }
1093110972 });
1093210973
1093310974 $('#advanced_div').on('click', function () {
@@ -10976,25 +11017,7 @@ jQuery(async function () {
1097611017 }, animation_duration);
1097711018
1097811019 if (popup_type == 'del_chat') {
10979- //close past chat popup
11020+ await handleDeleteChat(chat_file_for_del, selected_group, fromSlashCommand);
10980- $('#select_chat_cross').trigger('click');
10981- showLoader();
10982- if (selected_group) {
10983- await deleteGroupChat(selected_group, chat_file_for_del);
10984- } else {
10985- await delChat(chat_file_for_del);
10986- }
10987-
10988- if (fromSlashCommand) { // When called from `/delchat` command, don't re-open the history view.
10989- $('#options').hide(); // hide option popup menu
10990- hideLoader();
10991- } else { // Open the history view again after 2 seconds (delay to avoid edge cases for deleting last chat).
10992- setTimeout(function () {
10993- $('#option_select_chat').trigger('click');
10994- $('#options').hide(); // hide option popup menu
10995- hideLoader();
10996- }, 2000);
10997- }
1099811021 }
1099911022
1100011023 if (dialogueResolve) {
@@ -11457,7 +11480,7 @@ jQuery(async function () {
1145711480 is_delete_mode = false;
1145811481 });
1145911482
1146011483 $('#settings_preset').changeon('change', function () {
1146111484 if ($('#settings_preset').find(':selected').val() != 'gui') {
1146211485 preset_settings = $('#settings_preset').find(':selected').text();
1146311486 const preset = koboldai_settings[koboldai_setting_names[preset_settings]];
@@ -11482,7 +11505,7 @@ jQuery(async function () {
1148211505 saveSettingsDebounced();
1148311506 });
1148411507
1148511508 $('#settings_preset_novel').changeon('change', function () {
1148611509 nai_settings.preset_settings_novel = $('#settings_preset_novel')
1148711510 .find(':selected')
1148811511 .text();
@@ -11495,7 +11518,7 @@ jQuery(async function () {
1149511518 saveSettingsDebounced();
1149611519 });
1149711520
1149811521 $('#main_api').changeon('change', function () {
1149911522 cancelStatusCheck('Canceled because main api changed');
1150011523 changeMainAPI();
1150111524 saveSettingsDebounced();
@@ -12023,7 +12046,7 @@ jQuery(async function () {
1202312046 select_rm_characters();
1202412047 });
1202512048
1202612049 $('#dupe_button').clickon('click', async function () {
1202712050 await duplicateCharacter();
1202812051 });
1202912052
@@ -12223,18 +12246,18 @@ jQuery(async function () {
1222312246 }
1222412247 });
1222512248
1222612249 $(document).keyupon('keyup', function (e) {
1222712250 if (e.key === 'Escape') {
1222812251 const isEditVisible = $('#curEditTextarea').is(':visible') || $('.reasoning_edit_textarea').length > 0;
1222912252 if (isEditVisible && power_user.auto_save_msg_edits === false) {
1223012253 closeMessageEditor('all');
1223112254 $('#send_textarea').focustrigger('focus');
1223212255 return;
1223312256 }
1223412257 if (isEditVisible && power_user.auto_save_msg_edits === true) {
1223512258 $(`#chat .mes[mesid="${this_edit_mes_id}"] .mes_edit_done`).trigger('click');
1223612259 closeMessageEditor('reasoning');
1223712260 $('#send_textarea').focustrigger('focus');
1223812261 return;
1223912262 }
1224012263 if (!this_edit_mes_id && $('#mes_stop').is(':visible')) {
@@ -12251,13 +12274,13 @@ jQuery(async function () {
1225112274 const target = $(targetElement.selectedOptions).attr('id');
1225212275 switch (target) {
1225312276 case 'set_character_world':
1225412277 await openCharacterWorldPopup();
1225512278 break;
1225612279 case 'set_chat_scenario':
1225712280 await setScenarioOverride();
1225812281 break;
1225912282 case 'renameCharButton':
1226012283 await renameCharacter();
1226112284 break;
1226212285 case 'import_character_info':
1226312286 await importEmbeddedWorldInfo();
@@ -12335,6 +12358,9 @@ jQuery(async function () {
1233512358 });
1233612359
1233712360 $(document).on('change', '.range-block-counter input, .neo-range-input', function (e) {
12361+ if (!(e.target instanceof HTMLElement)) {
12362+ return;
12363+ }
1233812364 e.target.focus();
1233912365 e.target.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true }));
1234012366 });
@@ -12355,7 +12381,6 @@ jQuery(async function () {
1235512381 } else {
1235612382 //if value not ok, warn and reset to last known valid value
1235712383 toastr.warning(`Invalid value. Must be between ${$(this).attr('min')} and ${$(this).attr('max')}`);
12358- console.log(valueBeforeManualInput);
1235912384 //newSlider.val(valueBeforeManualInput)
1236012385 $(this).val(valueBeforeManualInput);
1236112386 }
@@ -12365,7 +12390,6 @@ jQuery(async function () {
1236512390
1236612391 $(document).on('keyup', '.range-block-counter input, .neo-range-input', function () {
1236712392 valueBeforeManualInput = $(this).val();
12368- console.log(valueBeforeManualInput);
1236912393 isManualInput = true;
1237012394 });
1237112395
@@ -12383,7 +12407,6 @@ jQuery(async function () {
1238312407 } else {
1238412408 //if value not ok, warn and reset to last known valid value
1238512409 toastr.warning(`Invalid value. Must be between ${$(this).attr('min')} and ${$(this).attr('max')}`);
12386- console.log(valueBeforeManualInput);
1238712410 $(this).val(valueBeforeManualInput);
1238812411 }
1238912412 }
public/scripts/RossAscends-mods.js+11 -11
@@ -434,19 +434,19 @@ function OpenNavPanels() {
434434 //auto-open R nav if locked and previously open
435435 if (accountStorage.getItem('NavLockOn') == 'true' && accountStorage.getItem('NavOpened') == 'true') {
436436 //console.log("RA -- clicking right nav to open");
437437 $('#rightNavDrawerIcon').clicktrigger('click');
438438 }
439439
440440 //auto-open L nav if locked and previously open
441441 if (accountStorage.getItem('LNavLockOn') == 'true' && accountStorage.getItem('LNavOpened') == 'true') {
442442 console.debug('RA -- clicking left nav to open');
443443 $('#leftNavDrawerIcon').clicktrigger('click');
444444 }
445445
446446 //auto-open WI if locked and previously open
447447 if (accountStorage.getItem('WINavLockOn') == 'true' && accountStorage.getItem('WINavOpened') == 'true') {
448448 console.debug('RA -- clicking WI to open');
449449 $('#WIDrawerIcon').clicktrigger('click');
450450 }
451451 }
452452}
@@ -711,7 +711,7 @@ export function initRossMods() {
711711 RA_autoconnect();
712712 }
713713
714714 $('#main_api').changeon('change', function () {
715715 var PrevAPI = main_api;
716716 setTimeout(() => RA_autoconnect(PrevAPI), 100);
717717 });
@@ -834,11 +834,11 @@ export function initRossMods() {
834834 });
835835
836836 var chatbarInFocus = false;
837837 $('#send_textarea').focuson('focus', function () {
838838 chatbarInFocus = true;
839839 });
840840
841841 $('#send_textarea').bluron('blur', function () {
842842 chatbarInFocus = false;
843843 });
844844
@@ -846,8 +846,8 @@ export function initRossMods() {
846846 OpenNavPanels();
847847 }, 300);
848848
849849 $(SelectedCharacterTab).clickon('click', function () { accountStorage.setItem('SelectedNavTab', 'rm_button_selected_ch'); });
850850 $('#rm_button_characters').clickon('click', function () { accountStorage.setItem('SelectedNavTab', 'rm_button_characters'); });
851851
852852 // when a char is selected from the list, save them as the auto-load character for next page load
853853
@@ -932,7 +932,7 @@ export function initRossMods() {
932932 var SwipeTargetMesClassParent = $(e.target).closest('.last_mes');
933933 if (SwipeTargetMesClassParent !== null) {
934934 if (SwipeButR.css('display') === 'flex') {
935935 SwipeButR.clicktrigger('click');
936936 }
937937 }
938938 });
@@ -956,7 +956,7 @@ export function initRossMods() {
956956 var SwipeTargetMesClassParent = $(e.target).closest('.last_mes');
957957 if (SwipeTargetMesClassParent !== null) {
958958 if (SwipeButL.css('display') === 'flex') {
959959 SwipeButL.clicktrigger('click');
960960 }
961961 }
962962 });
@@ -1159,7 +1159,7 @@ export function initRossMods() {
11591159 const lastMes = document.querySelector('.last_mes');
11601160 const editMes = lastMes.querySelector('.mes_block .mes_edit');
11611161 if (editMes !== null) {
11621162 $(editMes).clicktrigger('click');
11631163 return;
11641164 }
11651165 }
public/scripts/cfg-scale.js+1 -1
@@ -135,7 +135,7 @@ function onCfgMenuItemClick() {
135135 .siblings('.inline-drawer-content')
136136 .css('display') !== 'block') {
137137 $('#floatingPrompt').addClass('resizing');
138138 $('#CFGBlockToggle').clicktrigger('click');
139139 }
140140 } else {
141141 //hide AN if it's already displayed
public/scripts/chats.js+6 -5
@@ -44,6 +44,7 @@ import {
4444 download,
4545 getFileText,
4646 getFileExtension,
47+ convertTextToBase64,
4748} from './utils.js';
4849import { extension_settings, renderExtensionTemplateAsync, saveMetadataDebounced } from './extensions.js';
4950import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
@@ -224,7 +225,7 @@ export async function populateFileAttachment(message, inputId = 'file_form_input
224225 try {
225226 const converter = getConverter(file.type);
226227 const fileText = await converter(file);
227228 base64Data = window.btoa(unescape(encodeURIComponentconvertTextToBase64(fileText)));
228229 } catch (error) {
229230 toastr.error(String(error), t`Could not convert file`);
230231 console.error('Could not convert file', error);
@@ -477,7 +478,7 @@ export async function appendFileContent(message, messageText) {
477478export function encodeStyleTags(text) {
478479 const styleRegex = /<style>(.+?)<\/style>/gims;
479480 return text.replaceAll(styleRegex, (_, match) => {
480481 return `<custom-style>${escapeencodeURIComponent(match)}</custom-style>`;
481482 });
482483}
483484
@@ -530,7 +531,7 @@ export function decodeStyleTags(text, { prefix } = { prefix: '.mes_text ' }) {
530531
531532 return text.replaceAll(styleDecodeRegex, (_, style) => {
532533 try {
533534 let styleCleaned = unescapedecodeURIComponent(style).replaceAll(/<br\/>/g, '');
534535 const ast = css.parse(styleCleaned);
535536 const sheet = ast?.stylesheet;
536537 if (sheet) {
@@ -1497,14 +1498,14 @@ export async function uploadFileAttachmentToServer(file, target) {
14971498 try {
14981499 const converter = getConverter(file.type);
14991500 const fileText = await converter(file);
15001501 base64Data = window.btoa(unescape(encodeURIComponentconvertTextToBase64(fileText)));
15011502 } catch (error) {
15021503 toastr.error(String(error), t`Could not convert file`);
15031504 console.error('Could not convert file', error);
15041505 }
15051506 } else {
15061507 const fileText = await file.text();
15071508 base64Data = window.btoa(unescape(encodeURIComponentconvertTextToBase64(fileText)));
15081509 }
15091510
15101511 const fileUrl = await uploadFileAttachment(uniqueFileName, base64Data);
public/scripts/extensions/assets/index.js+2 -2
@@ -7,7 +7,7 @@ import { DOMPurify } from '../../../lib.js';
77import { getRequestHeaders, processDroppedFiles, eventSource, event_types } from '../../../script.js';
88import { deleteExtension, extensionNames, getContext, installExtension, renderExtensionTemplateAsync } from '../../extensions.js';
99import { POPUP_TYPE, Popup, callGenericPopup } from '../../popup.js';
1010import { executeSlashCommandsexecuteSlashCommandsWithOptions } from '../../slash-commands.js';
1111import { accountStorage } from '../../util/AccountStorage.js';
1212import { flashHighlight, getStringHash, isValidUrl } from '../../utils.js';
1313import { t } from '../../i18n.js';
@@ -142,7 +142,7 @@ async function downloadAssetsList(url) {
142142 const assetDelete = async function () {
143143 if (assetType === 'character') {
144144 toastr.error('Go to the characters menu to delete a character.', 'Character deletion not supported');
145145 await executeSlashCommandsexecuteSlashCommandsWithOptions(`/go ${asset['id']}`);
146146 return;
147147 }
148148 element.off('click');
public/scripts/extensions/token-counter/index.js+0 -13
@@ -9,19 +9,6 @@ import { POPUP_TYPE, callGenericPopup } from '../../popup.js';
99import { renderExtensionTemplateAsync } from '../../extensions.js';
1010import { t } from '../../i18n.js';
1111
12-function rgb2hex(rgb) {
13- rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
14- return (rgb && rgb.length === 4) ? '#' +
15- ('0' + parseInt(rgb[1], 10).toString(16)).slice(-2) +
16- ('0' + parseInt(rgb[2], 10).toString(16)).slice(-2) +
17- ('0' + parseInt(rgb[3], 10).toString(16)).slice(-2) : '';
18-}
19-
20-$('button').click(function () {
21- var hex = rgb2hex($('input').val());
22- $('.result').html(hex);
23-});
24-
2512async function doTokenCounter() {
2613 const { tokenizerName, tokenizerId } = getFriendlyTokenizerName(main_api);
2714 const html = await renderExtensionTemplateAsync('token-counter', 'window', { tokenizerName });
public/scripts/extensions/tts/alltalk.js+9 -9
@@ -296,20 +296,20 @@ class AllTalkTtsProvider {
296296 const ttsNarrateDialoguesCheckbox = document.getElementById('tts_narrate_dialogues');
297297 if (this.settings.narrator_enabled) {
298298 ttsPassAsterisksCheckbox.checked = false;
299299 $('#tts_pass_asterisks').clicktrigger('click');
300300 $('#tts_pass_asterisks').trigger('change');
301301 }
302302 if (!this.settings.narrator_enabled) {
303303 ttsPassAsterisksCheckbox.checked = true;
304304 $('#tts_pass_asterisks').clicktrigger('click');
305305 $('#tts_pass_asterisks').trigger('change');
306306 }
307307 if (this.settings.narrator_enabled) {
308308 ttsNarrateQuotedCheckbox.checked = true;
309309 ttsNarrateDialoguesCheckbox.checked = true;
310310 $('#tts_narrate_quoted').clicktrigger('click');
311311 $('#tts_narrate_quoted').trigger('change');
312312 $('#tts_narrate_dialogues').clicktrigger('click');
313313 $('#tts_narrate_dialogues').trigger('change');
314314 }
315315 atNarratorSelect.value = this.settings.narrator_enabled.toString();
@@ -783,21 +783,21 @@ class AllTalkTtsProvider {
783783
784784 if (narratorOption === 'true') {
785785 ttsPassAsterisksCheckbox.checked = false;
786786 $('#tts_pass_asterisks').clicktrigger('click');
787787 $('#tts_pass_asterisks').trigger('change');
788788 ttsNarrateQuotedCheckbox.checked = true;
789789 ttsNarrateDialoguesCheckbox.checked = true;
790790 $('#tts_narrate_quoted').clicktrigger('click');
791791 $('#tts_narrate_quoted').trigger('change');
792792 $('#tts_narrate_dialogues').clicktrigger('click');
793793 $('#tts_narrate_dialogues').trigger('change');
794794 } else if (narratorOption === 'silent') {
795795 ttsPassAsterisksCheckbox.checked = false;
796796 $('#tts_pass_asterisks').clicktrigger('click');
797797 $('#tts_pass_asterisks').trigger('change');
798798 } else {
799799 ttsPassAsterisksCheckbox.checked = true;
800800 $('#tts_pass_asterisks').clicktrigger('click');
801801 $('#tts_pass_asterisks').trigger('change');
802802 }
803803
public/scripts/nai-settings.js+1 -1
@@ -837,7 +837,7 @@ jQuery(function () {
837837 saveSettingsDebounced();
838838 });
839839
840840 $('#model_novel_select').changeon('change', function () {
841841 nai_settings.model_novel = String($('#model_novel_select').find(':selected').val());
842842 saveSettingsDebounced();
843843
public/scripts/power-user.js+9 -9
@@ -3151,7 +3151,7 @@ export function forceCharacterEditorTokenize() {
31513151 $('#character_popup').trigger('input');
31523152}
31533153
31543154$(document).readyjQuery(() => {
31553155 const adjustAutocompleteDebounced = debounce(() => {
31563156 $('.ui-autocomplete-input').each(function () {
31573157 const isOpen = $(this).autocomplete('widget')[0].style.display !== 'none';
@@ -3236,7 +3236,7 @@ $(document).ready(() => {
32363236 });
32373237
32383238 // Settings that go to settings.json
32393239 $('#collapse-newlines-checkbox').changeon('change', function () {
32403240 power_user.collapse_newlines = !!$(this).prop('checked');
32413241 saveSettingsDebounced();
32423242 });
@@ -3244,7 +3244,7 @@ $(document).ready(() => {
32443244 // include newline is the child of trim sentences
32453245 // if include newline is checked, trim sentences must be checked
32463246 // if trim sentences is unchecked, include newline must be unchecked
32473247 $('#trim_sentences_checkbox').changeon('change', function () {
32483248 power_user.trim_sentences = !!$(this).prop('checked');
32493249 saveSettingsDebounced();
32503250 });
@@ -3293,7 +3293,7 @@ $(document).ready(() => {
32933293
32943294 $('#bind_model_templates').on('change', updateBindModelTemplatesState);
32953295
32963296 $('#always-force-name2-checkbox').changeon('change', function () {
32973297 power_user.always_force_name2 = !!$(this).prop('checked');
32983298 saveSettingsDebounced();
32993299 });
@@ -3309,7 +3309,7 @@ $(document).ready(() => {
33093309 saveSettingsDebounced();
33103310 });
33113311
33123312 $('#chat-show-reply-prefix-checkbox').changeon('change', function () {
33133313 power_user.show_user_prompt_bias = !!$(this).prop('checked');
33143314 reloadCurrentChat();
33153315 saveSettingsDebounced();
@@ -3355,7 +3355,7 @@ $(document).ready(() => {
33553355 saveSettingsDebounced();
33563356 });
33573357
33583358 $('#fast_ui_mode').changeon('change', function () {
33593359 power_user.fast_ui_mode = $(this).prop('checked');
33603360 switchUiMode();
33613361 saveSettingsDebounced();
@@ -3373,13 +3373,13 @@ $(document).ready(() => {
33733373 applyCustomCSS();
33743374 });
33753375
33763376 $('#movingUImode').changeon('change', function () {
33773377 power_user.movingUI = $(this).prop('checked');
33783378 switchMovingUI();
33793379 saveSettingsDebounced();
33803380 });
33813381
33823382 $('#noShadowsmode').changeon('change', function () {
33833383 power_user.noShadows = $(this).prop('checked');
33843384 applyNoShadows();
33853385 saveSettingsDebounced();
@@ -3848,7 +3848,7 @@ $(document).ready(() => {
38483848 saveSettingsDebounced();
38493849 });
38503850
38513851 $('#custom_stopping_strings_macro').changeon('change', function () {
38523852 power_user.custom_stopping_strings_macro = !!$(this).prop('checked');
38533853 saveSettingsDebounced();
38543854 });
public/scripts/server-history.js+1 -1
@@ -46,7 +46,7 @@ function createServerAutocomplete() {
4646 select: (e, u) => selectServer(e, u, serverLabel),
4747 minLength: 0,
4848 })
4949 .focuson('focus', onInputFocus); // <== show tag list on click
5050}
5151
5252function onInputFocus() {
public/scripts/tags.js+1 -1
@@ -1301,7 +1301,7 @@ export function createTagInput(inputSelector, listSelector, tagListOptions = {})
13011301 select: (e, u) => selectTag(e, u, listSelector, { tagListOptions: tagListOptions }),
13021302 minLength: 0,
13031303 })
13041304 .focuson('focus', onTagInputFocus); // <== show tag list on click
13051305}
13061306
13071307async function onViewTagsListClick() {
public/scripts/utils.js+21 -0
@@ -1453,6 +1453,27 @@ export function getFileExtension(file) {
14531453}
14541454
14551455/**
1456+ * Converts UTF-8 string into Base64-encoded string.
1457+ *
1458+ * @param {string} text The UTF-8 string
1459+ * @returns {string} The Base64-encoded string
1460+ */
1461+export function convertTextToBase64(text) {
1462+ const encoder = new TextEncoder();
1463+ const utf8Bytes = encoder.encode(text);
1464+ /**
1465+ * return `true` if `Uint8Array.prototype.toBase64` function is supported.
1466+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/toBase64|MDN Reference}
1467+ */
1468+ if ('toBase64' in Uint8Array.prototype) {
1469+ return utf8Bytes.toBase64();
1470+ }
1471+ // Creates binary string, where each character's code point directly matches the byte value (0-255).
1472+ const binaryString = String.fromCharCode(...utf8Bytes);
1473+ return window.btoa(binaryString);
1474+}
1475+
1476+/**
14561477 * Loads either a CSS or JS file and appends it to the appropriate document section.
14571478 *
14581479 * @param {string} url - The URL of the file to be loaded.