Merge pull request #3463 from SillyTavern/bogus-localstorage Add settings.json-backed KV string storage

43f524bb5f4ffebfd3bf5140e50f5fc8f34051f9

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

Signed
18 files changed, +251 -108Ignore whitespace
public/script.js+17 -10
@@ -270,6 +270,7 @@ import { initBulkEdit } from './scripts/bulk-edit.js';
270import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js';270import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js';
271import { getContext } from './scripts/st-context.js';271import { getContext } from './scripts/st-context.js';
272import { extractReasoningFromData, initReasoning, PromptReasoning, removeReasoningFromString, updateReasoningTimeUI } from './scripts/reasoning.js';272import { extractReasoningFromData, initReasoning, PromptReasoning, removeReasoningFromString, updateReasoningTimeUI } from './scripts/reasoning.js';
273import { accountStorage } from './scripts/util/AccountStorage.js';
273274
274// API OBJECT FOR EXTERNAL WIRING275// API OBJECT FOR EXTERNAL WIRING
275globalThis.SillyTavern = {276globalThis.SillyTavern = {
@@ -419,7 +420,7 @@ DOMPurify.addHook('uponSanitizeElement', (node, _, config) => {
419 const entityId = getCurrentEntityId();420 const entityId = getCurrentEntityId();
420 const warningShownKey = `mediaWarningShown:${entityId}`;421 const warningShownKey = `mediaWarningShown:${entityId}`;
421422
422 if (localStorage.getItem(warningShownKey) === null) {423 if (accountStorage.getItem(warningShownKey) === null) {
423 const warningToast = toastr.warning(424 const warningToast = toastr.warning(
424 t`Use the 'Ext. Media' button to allow it. Click on this message to dismiss.`,425 t`Use the 'Ext. Media' button to allow it. Click on this message to dismiss.`,
425 t`External media has been blocked`,426 t`External media has been blocked`,
@@ -430,7 +431,7 @@ DOMPurify.addHook('uponSanitizeElement', (node, _, config) => {
430 },431 },
431 );432 );
432433
433 localStorage.setItem(warningShownKey, 'true');434 accountStorage.setItem(warningShownKey, 'true');
434 }435 }
435 }436 }
436});437});
@@ -1490,7 +1491,7 @@ export async function printCharacters(fullRefresh = false) {
14901491
1491 $('#rm_print_characters_pagination').pagination({1492 $('#rm_print_characters_pagination').pagination({
1492 dataSource: entities,1493 dataSource: entities,
1493 pageSize: Number(localStorage.getItem(storageKey)) || per_page_default,1494 pageSize: Number(accountStorage.getItem(storageKey)) || per_page_default,
1494 sizeChangerOptions: [10, 25, 50, 100, 250, 500, 1000],1495 sizeChangerOptions: [10, 25, 50, 100, 250, 500, 1000],
1495 pageRange: 1,1496 pageRange: 1,
1496 pageNumber: saveCharactersPage || 1,1497 pageNumber: saveCharactersPage || 1,
@@ -1534,7 +1535,7 @@ export async function printCharacters(fullRefresh = false) {
1534 eventSource.emit(event_types.CHARACTER_PAGE_LOADED);1535 eventSource.emit(event_types.CHARACTER_PAGE_LOADED);
1535 },1536 },
1536 afterSizeSelectorChange: function (e) {1537 afterSizeSelectorChange: function (e) {
1537 localStorage.setItem(storageKey, e.target.value);1538 accountStorage.setItem(storageKey, e.target.value);
1538 },1539 },
1539 afterPaging: function (e) {1540 afterPaging: function (e) {
1540 saveCharactersPage = e;1541 saveCharactersPage = e;
@@ -6903,10 +6904,11 @@ export async function getSettings() {
6903 $('#your_name').val(name1);6904 $('#your_name').val(name1);
6904 }6905 }
69056906
6907 accountStorage.init(settings?.accountStorage);
6906 await setUserControls(data.enable_accounts);6908 await setUserControls(data.enable_accounts);
69076909
6908 // Allow subscribers to mutate settings6910 // Allow subscribers to mutate settings
6909 eventSource.emit(event_types.SETTINGS_LOADED_BEFORE, settings);6911 await eventSource.emit(event_types.SETTINGS_LOADED_BEFORE, settings);
69106912
6911 //Load KoboldAI settings6913 //Load KoboldAI settings
6912 koboldai_setting_names = data.koboldai_setting_names;6914 koboldai_setting_names = data.koboldai_setting_names;
@@ -7074,7 +7076,8 @@ function selectKoboldGuiPreset() {
70747076
7075export async function saveSettings(loopCounter = 0) {7077export async function saveSettings(loopCounter = 0) {
7076 if (!settingsReady) {7078 if (!settingsReady) {
7077 console.warn('Settings not ready, aborting save');7079 console.warn('Settings not ready, scheduling another save');
7080 saveSettingsDebounced();
7078 return;7081 return;
7079 }7082 }
70807083
@@ -7095,6 +7098,7 @@ export async function saveSettings(loopCounter = 0) {
7095 url: '/api/settings/save',7098 url: '/api/settings/save',
7096 data: JSON.stringify({7099 data: JSON.stringify({
7097 firstRun: firstRun,7100 firstRun: firstRun,
7101 accountStorage: accountStorage.getState(),
7098 currentVersion: currentVersion,7102 currentVersion: currentVersion,
7099 username: name1,7103 username: name1,
7100 active_character: active_character,7104 active_character: active_character,
@@ -7550,7 +7554,7 @@ export function select_rm_info(type, charId, previousCharId = null) {
7550 }7554 }
75517555
7552 try {7556 try {
7553 const perPage = Number(localStorage.getItem('Characters_PerPage')) || per_page_default;7557 const perPage = Number(accountStorage.getItem('Characters_PerPage')) || per_page_default;
7554 const page = Math.floor(charIndex / perPage) + 1;7558 const page = Math.floor(charIndex / perPage) + 1;
7555 const selector = `#rm_print_characters_block [title*="${avatarFileName}"]`;7559 const selector = `#rm_print_characters_block [title*="${avatarFileName}"]`;
7556 $('#rm_print_characters_pagination').pagination('go', page);7560 $('#rm_print_characters_pagination').pagination('go', page);
@@ -7582,7 +7586,7 @@ export function select_rm_info(type, charId, previousCharId = null) {
7582 return;7586 return;
7583 }7587 }
75847588
7585 const perPage = Number(localStorage.getItem('Characters_PerPage')) || per_page_default;7589 const perPage = Number(accountStorage.getItem('Characters_PerPage')) || per_page_default;
7586 const page = Math.floor(charIndex / perPage) + 1;7590 const page = Math.floor(charIndex / perPage) + 1;
7587 $('#rm_print_characters_pagination').pagination('go', page);7591 $('#rm_print_characters_pagination').pagination('go', page);
7588 const selector = `#rm_print_characters_block [grid="${charId}"]`;7592 const selector = `#rm_print_characters_block [grid="${charId}"]`;
@@ -9449,6 +9453,9 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {})
9449 continue;9453 continue;
9450 }9454 }
94519455
9456 accountStorage.removeItem(`AlertWI_${character.avatar}`);
9457 accountStorage.removeItem(`AlertRegex_${character.avatar}`);
9458 accountStorage.removeItem(`mediaWarningShown:${character.avatar}`);
9452 delete tag_map[character.avatar];9459 delete tag_map[character.avatar];
9453 select_rm_info('char_delete', character.name);9460 select_rm_info('char_delete', character.name);
94549461
@@ -9651,8 +9658,8 @@ function addDebugFunctions() {
9651 });9658 });
96529659
9653 registerDebugFunction('toggleRegenerateWarning', 'Toggle Ctrl+Enter regeneration confirmation', 'Toggle the warning when regenerating a message with a Ctrl+Enter hotkey.', () => {9660 registerDebugFunction('toggleRegenerateWarning', 'Toggle Ctrl+Enter regeneration confirmation', 'Toggle the warning when regenerating a message with a Ctrl+Enter hotkey.', () => {
9654 localStorage.setItem('RegenerateWithCtrlEnter', localStorage.getItem('RegenerateWithCtrlEnter') === 'true' ? 'false' : 'true');9661 accountStorage.setItem('RegenerateWithCtrlEnter', accountStorage.getItem('RegenerateWithCtrlEnter') === 'true' ? 'false' : 'true');
9655 toastr.info('Regenerate warning is now ' + (localStorage.getItem('RegenerateWithCtrlEnter') === 'true' ? 'disabled' : 'enabled'));9662 toastr.info('Regenerate warning is now ' + (accountStorage.getItem('RegenerateWithCtrlEnter') === 'true' ? 'disabled' : 'enabled'));
9656 });9663 });
96579664
9658 registerDebugFunction('copySetup', 'Copy ST setup to clipboard [WIP]', 'Useful data when reporting bugs', async () => {9665 registerDebugFunction('copySetup', 'Copy ST setup to clipboard [WIP]', 'Useful data when reporting bugs', async () => {
public/scripts/RossAscends-mods.js+29 -25
@@ -27,7 +27,6 @@ import {
27 send_on_enter_options,27 send_on_enter_options,
28} from './power-user.js';28} from './power-user.js';
2929
30import { LoadLocal, SaveLocal, LoadLocalBool } from './f-localStorage.js';
31import { selected_group, is_group_generating, openGroupById } from './group-chats.js';30import { selected_group, is_group_generating, openGroupById } from './group-chats.js';
32import { getTagKeyForEntity, applyTagsOnCharacterSelect } from './tags.js';31import { getTagKeyForEntity, applyTagsOnCharacterSelect } from './tags.js';
33import {32import {
@@ -41,6 +40,8 @@ import { textgen_types, textgenerationwebui_settings as textgen_settings, getTex
41import { debounce_timeout } from './constants.js';40import { debounce_timeout } from './constants.js';
4241
43import { Popup } from './popup.js';42import { Popup } from './popup.js';
43import { accountStorage } from './util/AccountStorage.js';
44import { getCurrentUserHandle } from './user.js';
4445
45var RPanelPin = document.getElementById('rm_button_panel_pin');46var RPanelPin = document.getElementById('rm_button_panel_pin');
46var LPanelPin = document.getElementById('lm_button_panel_pin');47var LPanelPin = document.getElementById('lm_button_panel_pin');
@@ -409,32 +410,34 @@ function RA_autoconnect(PrevApi) {
409function OpenNavPanels() {410function OpenNavPanels() {
410 if (!isMobile()) {411 if (!isMobile()) {
411 //auto-open R nav if locked and previously open412 //auto-open R nav if locked and previously open
412 if (LoadLocalBool('NavLockOn') == true && LoadLocalBool('NavOpened') == true) {413 if (accountStorage.getItem('NavLockOn') == 'true' && accountStorage.getItem('NavOpened') == 'true') {
413 //console.log("RA -- clicking right nav to open");414 //console.log("RA -- clicking right nav to open");
414 $('#rightNavDrawerIcon').click();415 $('#rightNavDrawerIcon').click();
415 }416 }
416417
417 //auto-open L nav if locked and previously open418 //auto-open L nav if locked and previously open
418 if (LoadLocalBool('LNavLockOn') == true && LoadLocalBool('LNavOpened') == true) {419 if (accountStorage.getItem('LNavLockOn') == 'true' && accountStorage.getItem('LNavOpened') == 'true') {
419 console.debug('RA -- clicking left nav to open');420 console.debug('RA -- clicking left nav to open');
420 $('#leftNavDrawerIcon').click();421 $('#leftNavDrawerIcon').click();
421 }422 }
422423
423 //auto-open WI if locked and previously open424 //auto-open WI if locked and previously open
424 if (LoadLocalBool('WINavLockOn') == true && LoadLocalBool('WINavOpened') == true) {425 if (accountStorage.getItem('WINavLockOn') == 'true' && accountStorage.getItem('WINavOpened') == 'true') {
425 console.debug('RA -- clicking WI to open');426 console.debug('RA -- clicking WI to open');
426 $('#WIDrawerIcon').click();427 $('#WIDrawerIcon').click();
427 }428 }
428 }429 }
429}430}
430431
432const getUserInputKey = () => getCurrentUserHandle() + '_userInput';
433
431function restoreUserInput() {434function restoreUserInput() {
432 if (!power_user.restore_user_input) {435 if (!power_user.restore_user_input) {
433 console.debug('restoreUserInput disabled');436 console.debug('restoreUserInput disabled');
434 return;437 return;
435 }438 }
436439
437 const userInput = LoadLocal('userInput');440 const userInput = localStorage.getItem(getUserInputKey());
438 if (userInput) {441 if (userInput) {
439 $('#send_textarea').val(userInput)[0].dispatchEvent(new Event('input', { bubbles: true }));442 $('#send_textarea').val(userInput)[0].dispatchEvent(new Event('input', { bubbles: true }));
440 }443 }
@@ -442,7 +445,8 @@ function restoreUserInput() {
442445
443function saveUserInput() {446function saveUserInput() {
444 const userInput = String($('#send_textarea').val());447 const userInput = String($('#send_textarea').val());
445 SaveLocal('userInput', userInput);448 localStorage.setItem(getUserInputKey(), userInput);
449 console.debug('User Input -- ', userInput);
446}450}
447const saveUserInputDebounced = debounce(saveUserInput);451const saveUserInputDebounced = debounce(saveUserInput);
448452
@@ -739,7 +743,7 @@ export function initRossMods() {
739743
740 //toggle pin class when lock toggle clicked744 //toggle pin class when lock toggle clicked
741 $(RPanelPin).on('click', function () {745 $(RPanelPin).on('click', function () {
742 SaveLocal('NavLockOn', $(RPanelPin).prop('checked'));746 accountStorage.setItem('NavLockOn', $(RPanelPin).prop('checked'));
743 if ($(RPanelPin).prop('checked') == true) {747 if ($(RPanelPin).prop('checked') == true) {
744 //console.log('adding pin class to right nav');748 //console.log('adding pin class to right nav');
745 $(RightNavPanel).addClass('pinnedOpen');749 $(RightNavPanel).addClass('pinnedOpen');
@@ -757,7 +761,7 @@ export function initRossMods() {
757 }761 }
758 });762 });
759 $(LPanelPin).on('click', function () {763 $(LPanelPin).on('click', function () {
760 SaveLocal('LNavLockOn', $(LPanelPin).prop('checked'));764 accountStorage.setItem('LNavLockOn', $(LPanelPin).prop('checked'));
761 if ($(LPanelPin).prop('checked') == true) {765 if ($(LPanelPin).prop('checked') == true) {
762 //console.log('adding pin class to Left nav');766 //console.log('adding pin class to Left nav');
763 $(LeftNavPanel).addClass('pinnedOpen');767 $(LeftNavPanel).addClass('pinnedOpen');
@@ -776,7 +780,7 @@ export function initRossMods() {
776 });780 });
777781
778 $(WIPanelPin).on('click', function () {782 $(WIPanelPin).on('click', function () {
779 SaveLocal('WINavLockOn', $(WIPanelPin).prop('checked'));783 accountStorage.setItem('WINavLockOn', $(WIPanelPin).prop('checked'));
780 if ($(WIPanelPin).prop('checked') == true) {784 if ($(WIPanelPin).prop('checked') == true) {
781 console.debug('adding pin class to WI');785 console.debug('adding pin class to WI');
782 $(WorldInfo).addClass('pinnedOpen');786 $(WorldInfo).addClass('pinnedOpen');
@@ -796,8 +800,8 @@ export function initRossMods() {
796 });800 });
797801
798 // read the state of right Nav Lock and apply to rightnav classlist802 // read the state of right Nav Lock and apply to rightnav classlist
799 $(RPanelPin).prop('checked', LoadLocalBool('NavLockOn'));803 $(RPanelPin).prop('checked', accountStorage.getItem('NavLockOn') == 'true');
800 if (LoadLocalBool('NavLockOn') == true) {804 if (accountStorage.getItem('NavLockOn') == 'true') {
801 //console.log('setting pin class via local var');805 //console.log('setting pin class via local var');
802 $(RightNavPanel).addClass('pinnedOpen');806 $(RightNavPanel).addClass('pinnedOpen');
803 $(RightNavDrawerIcon).addClass('drawerPinnedOpen');807 $(RightNavDrawerIcon).addClass('drawerPinnedOpen');
@@ -808,8 +812,8 @@ export function initRossMods() {
808 $(RightNavDrawerIcon).addClass('drawerPinnedOpen');812 $(RightNavDrawerIcon).addClass('drawerPinnedOpen');
809 }813 }
810 // read the state of left Nav Lock and apply to leftnav classlist814 // read the state of left Nav Lock and apply to leftnav classlist
811 $(LPanelPin).prop('checked', LoadLocalBool('LNavLockOn'));815 $(LPanelPin).prop('checked', accountStorage.getItem('LNavLockOn') === 'true');
812 if (LoadLocalBool('LNavLockOn') == true) {816 if (accountStorage.getItem('LNavLockOn') == 'true') {
813 //console.log('setting pin class via local var');817 //console.log('setting pin class via local var');
814 $(LeftNavPanel).addClass('pinnedOpen');818 $(LeftNavPanel).addClass('pinnedOpen');
815 $(LeftNavDrawerIcon).addClass('drawerPinnedOpen');819 $(LeftNavDrawerIcon).addClass('drawerPinnedOpen');
@@ -821,8 +825,8 @@ export function initRossMods() {
821 }825 }
822826
823 // read the state of left Nav Lock and apply to leftnav classlist827 // read the state of left Nav Lock and apply to leftnav classlist
824 $(WIPanelPin).prop('checked', LoadLocalBool('WINavLockOn'));828 $(WIPanelPin).prop('checked', accountStorage.getItem('WINavLockOn') === 'true');
825 if (LoadLocalBool('WINavLockOn') == true) {829 if (accountStorage.getItem('WINavLockOn') == 'true') {
826 //console.log('setting pin class via local var');830 //console.log('setting pin class via local var');
827 $(WorldInfo).addClass('pinnedOpen');831 $(WorldInfo).addClass('pinnedOpen');
828 $(WIDrawerIcon).addClass('drawerPinnedOpen');832 $(WIDrawerIcon).addClass('drawerPinnedOpen');
@@ -837,22 +841,22 @@ export function initRossMods() {
837 //save state of Right nav being open or closed841 //save state of Right nav being open or closed
838 $('#rightNavDrawerIcon').on('click', function () {842 $('#rightNavDrawerIcon').on('click', function () {
839 if (!$('#rightNavDrawerIcon').hasClass('openIcon')) {843 if (!$('#rightNavDrawerIcon').hasClass('openIcon')) {
840 SaveLocal('NavOpened', 'true');844 accountStorage.setItem('NavOpened', 'true');
841 } else { SaveLocal('NavOpened', 'false'); }845 } else { accountStorage.setItem('NavOpened', 'false'); }
842 });846 });
843847
844 //save state of Left nav being open or closed848 //save state of Left nav being open or closed
845 $('#leftNavDrawerIcon').on('click', function () {849 $('#leftNavDrawerIcon').on('click', function () {
846 if (!$('#leftNavDrawerIcon').hasClass('openIcon')) {850 if (!$('#leftNavDrawerIcon').hasClass('openIcon')) {
847 SaveLocal('LNavOpened', 'true');851 accountStorage.setItem('LNavOpened', 'true');
848 } else { SaveLocal('LNavOpened', 'false'); }852 } else { accountStorage.setItem('LNavOpened', 'false'); }
849 });853 });
850854
851 //save state of Left nav being open or closed855 //save state of Left nav being open or closed
852 $('#WorldInfo').on('click', function () {856 $('#WorldInfo').on('click', function () {
853 if (!$('#WorldInfo').hasClass('openIcon')) {857 if (!$('#WorldInfo').hasClass('openIcon')) {
854 SaveLocal('WINavOpened', 'true');858 accountStorage.setItem('WINavOpened', 'true');
855 } else { SaveLocal('WINavOpened', 'false'); }859 } else { accountStorage.setItem('WINavOpened', 'false'); }
856 });860 });
857861
858 var chatbarInFocus = false;862 var chatbarInFocus = false;
@@ -868,8 +872,8 @@ export function initRossMods() {
868 OpenNavPanels();872 OpenNavPanels();
869 }, 300);873 }, 300);
870874
871 $(SelectedCharacterTab).click(function () { SaveLocal('SelectedNavTab', 'rm_button_selected_ch'); });875 $(SelectedCharacterTab).click(function () { accountStorage.setItem('SelectedNavTab', 'rm_button_selected_ch'); });
872 $('#rm_button_characters').click(function () { SaveLocal('SelectedNavTab', 'rm_button_characters'); });876 $('#rm_button_characters').click(function () { accountStorage.setItem('SelectedNavTab', 'rm_button_characters'); });
873877
874 // when a char is selected from the list, save them as the auto-load character for next page load878 // when a char is selected from the list, save them as the auto-load character for next page load
875879
@@ -1077,7 +1081,7 @@ export function initRossMods() {
1077 }1081 }
1078 else if (is_send_press == false) {1082 else if (is_send_press == false) {
1079 const skipConfirmKey = 'RegenerateWithCtrlEnter';1083 const skipConfirmKey = 'RegenerateWithCtrlEnter';
1080 const skipConfirm = LoadLocalBool(skipConfirmKey);1084 const skipConfirm = accountStorage.getItem(skipConfirmKey) === 'true';
1081 function doRegenerate() {1085 function doRegenerate() {
1082 console.debug('Regenerating with Ctrl+Enter');1086 console.debug('Regenerating with Ctrl+Enter');
1083 $('#option_regenerate').trigger('click');1087 $('#option_regenerate').trigger('click');
@@ -1097,7 +1101,7 @@ export function initRossMods() {
1097 return;1101 return;
1098 }1102 }
10991103
1100 SaveLocal(skipConfirmKey, regenerateWithCtrlEnter);1104 accountStorage.setItem(skipConfirmKey, String(regenerateWithCtrlEnter));
1101 doRegenerate();1105 doRegenerate();
1102 }1106 }
1103 return;1107 return;
public/scripts/chats.js+5 -4
@@ -45,6 +45,7 @@ import { DragAndDropHandler } from './dragdrop.js';
45import { renderTemplateAsync } from './templates.js';45import { renderTemplateAsync } from './templates.js';
46import { t } from './i18n.js';46import { t } from './i18n.js';
47import { humanizedDateTime } from './RossAscends-mods.js';47import { humanizedDateTime } from './RossAscends-mods.js';
48import { accountStorage } from './util/AccountStorage.js';
4849
49/**50/**
50 * @typedef {Object} FileAttachment51 * @typedef {Object} FileAttachment
@@ -1078,8 +1079,8 @@ async function openAttachmentManager() {
1078 renderAttachments();1079 renderAttachments();
1079 });1080 });
10801081
1081 let sortField = localStorage.getItem('DataBank_sortField') || 'created';1082 let sortField = accountStorage.getItem('DataBank_sortField') || 'created';
1082 let sortOrder = localStorage.getItem('DataBank_sortOrder') || 'desc';1083 let sortOrder = accountStorage.getItem('DataBank_sortOrder') || 'desc';
1083 let filterString = '';1084 let filterString = '';
10841085
1085 const template = $(await renderExtensionTemplateAsync('attachments', 'manager', {}));1086 const template = $(await renderExtensionTemplateAsync('attachments', 'manager', {}));
@@ -1095,8 +1096,8 @@ async function openAttachmentManager() {
10951096
1096 sortField = this.selectedOptions[0].dataset.sortField;1097 sortField = this.selectedOptions[0].dataset.sortField;
1097 sortOrder = this.selectedOptions[0].dataset.sortOrder;1098 sortOrder = this.selectedOptions[0].dataset.sortOrder;
1098 localStorage.setItem('DataBank_sortField', sortField);1099 accountStorage.setItem('DataBank_sortField', sortField);
1099 localStorage.setItem('DataBank_sortOrder', sortOrder);1100 accountStorage.setItem('DataBank_sortOrder', sortOrder);
1100 renderAttachments();1101 renderAttachments();
1101 });1102 });
1102 function handleBulkAction(action) {1103 function handleBulkAction(action) {
public/scripts/extensions.js+5 -4
@@ -9,6 +9,7 @@ import { getContext } from './st-context.js';
9import { isAdmin } from './user.js';9import { isAdmin } from './user.js';
10import { t } from './i18n.js';10import { t } from './i18n.js';
11import { debounce_timeout } from './constants.js';11import { debounce_timeout } from './constants.js';
12import { accountStorage } from './util/AccountStorage.js';
1213
13export {14export {
14 getContext,15 getContext,
@@ -714,7 +715,7 @@ async function showExtensionsDetails() {
714 htmlExternal.append(htmlLoading);715 htmlExternal.append(htmlLoading);
715716
716 const sortOrderKey = 'extensions_sortByName';717 const sortOrderKey = 'extensions_sortByName';
717 const sortByName = localStorage.getItem(sortOrderKey) === 'true';718 const sortByName = accountStorage.getItem(sortOrderKey) === 'true';
718 const sortFn = sortByName ? sortManifestsByName : sortManifestsByOrder;719 const sortFn = sortByName ? sortManifestsByName : sortManifestsByOrder;
719 const extensions = Object.entries(manifests).sort((a, b) => sortFn(a[1], b[1])).map(getExtensionData);720 const extensions = Object.entries(manifests).sort((a, b) => sortFn(a[1], b[1])).map(getExtensionData);
720721
@@ -745,7 +746,7 @@ async function showExtensionsDetails() {
745 text: sortByName ? t`Sort: Display Name` : t`Sort: Loading Order`,746 text: sortByName ? t`Sort: Display Name` : t`Sort: Loading Order`,
746 action: async () => {747 action: async () => {
747 abortController.abort();748 abortController.abort();
748 localStorage.setItem(sortOrderKey, sortByName ? 'false' : 'true');749 accountStorage.setItem(sortOrderKey, sortByName ? 'false' : 'true');
749 await showExtensionsDetails();750 await showExtensionsDetails();
750 },751 },
751 };752 };
@@ -1153,11 +1154,11 @@ async function checkForExtensionUpdates(force) {
1153 const currentDate = new Date().toDateString();1154 const currentDate = new Date().toDateString();
11541155
1155 // Don't nag more than once a day1156 // Don't nag more than once a day
1156 if (localStorage.getItem(STORAGE_NAG_KEY) === currentDate) {1157 if (accountStorage.getItem(STORAGE_NAG_KEY) === currentDate) {
1157 return;1158 return;
1158 }1159 }
11591160
1160 localStorage.setItem(STORAGE_NAG_KEY, currentDate);1161 accountStorage.setItem(STORAGE_NAG_KEY, currentDate);
1161 }1162 }
11621163
1163 const isCurrentUserAdmin = isAdmin();1164 const isCurrentUserAdmin = isAdmin();
public/scripts/extensions/assets/index.js+3 -2
@@ -8,6 +8,7 @@ import { getRequestHeaders, processDroppedFiles, eventSource, event_types } from
8import { deleteExtension, extensionNames, getContext, installExtension, renderExtensionTemplateAsync } from '../../extensions.js';8import { deleteExtension, extensionNames, getContext, installExtension, renderExtensionTemplateAsync } from '../../extensions.js';
9import { POPUP_TYPE, Popup, callGenericPopup } from '../../popup.js';9import { POPUP_TYPE, Popup, callGenericPopup } from '../../popup.js';
10import { executeSlashCommands } from '../../slash-commands.js';10import { executeSlashCommands } from '../../slash-commands.js';
11import { accountStorage } from '../../util/AccountStorage.js';
11import { flashHighlight, getStringHash, isValidUrl } from '../../utils.js';12import { flashHighlight, getStringHash, isValidUrl } from '../../utils.js';
12export { MODULE_NAME };13export { MODULE_NAME };
1314
@@ -432,14 +433,14 @@ jQuery(async () => {
432 connectButton.on('click', async function () {433 connectButton.on('click', async function () {
433 const url = DOMPurify.sanitize(String(assetsJsonUrl.val()));434 const url = DOMPurify.sanitize(String(assetsJsonUrl.val()));
434 const rememberKey = `Assets_SkipConfirm_${getStringHash(url)}`;435 const rememberKey = `Assets_SkipConfirm_${getStringHash(url)}`;
435 const skipConfirm = localStorage.getItem(rememberKey) === 'true';436 const skipConfirm = accountStorage.getItem(rememberKey) === 'true';
436437
437 const confirmation = skipConfirm || await Popup.show.confirm('Loading Asset List', `<span>Are you sure you want to connect to the following url?</span><var>${url}</var>`, {438 const confirmation = skipConfirm || await Popup.show.confirm('Loading Asset List', `<span>Are you sure you want to connect to the following url?</span><var>${url}</var>`, {
438 customInputs: [{ id: 'assets-remember', label: 'Don\'t ask again for this URL' }],439 customInputs: [{ id: 'assets-remember', label: 'Don\'t ask again for this URL' }],
439 onClose: popup => {440 onClose: popup => {
440 if (popup.result) {441 if (popup.result) {
441 const rememberValue = popup.inputResults.get('assets-remember');442 const rememberValue = popup.inputResults.get('assets-remember');
442 localStorage.setItem(rememberKey, String(rememberValue));443 accountStorage.setItem(rememberKey, String(rememberValue));
443 }444 }
444 },445 },
445 });446 });
public/scripts/extensions/quick-reply/src/QuickReply.js+9 -8
@@ -10,6 +10,7 @@ import { SlashCommandExecutor } from '../../../slash-commands/SlashCommandExecut
10import { SlashCommandParser } from '../../../slash-commands/SlashCommandParser.js';10import { SlashCommandParser } from '../../../slash-commands/SlashCommandParser.js';
11import { SlashCommandParserError } from '../../../slash-commands/SlashCommandParserError.js';11import { SlashCommandParserError } from '../../../slash-commands/SlashCommandParserError.js';
12import { SlashCommandScope } from '../../../slash-commands/SlashCommandScope.js';12import { SlashCommandScope } from '../../../slash-commands/SlashCommandScope.js';
13import { accountStorage } from '../../../util/AccountStorage.js';
13import { debounce, delay, getSortableDelay, showFontAwesomePicker } from '../../../utils.js';14import { debounce, delay, getSortableDelay, showFontAwesomePicker } from '../../../utils.js';
14import { log, quickReplyApi, warn } from '../index.js';15import { log, quickReplyApi, warn } from '../index.js';
15import { QuickReplyContextLink } from './QuickReplyContextLink.js';16import { QuickReplyContextLink } from './QuickReplyContextLink.js';
@@ -544,9 +545,9 @@ export class QuickReply {
544 this.editorSyntax = messageSyntaxInner;545 this.editorSyntax = messageSyntaxInner;
545 /**@type {HTMLInputElement}*/546 /**@type {HTMLInputElement}*/
546 const wrap = dom.querySelector('#qr--modal-wrap');547 const wrap = dom.querySelector('#qr--modal-wrap');
547 wrap.checked = JSON.parse(localStorage.getItem('qr--wrap') ?? 'false');548 wrap.checked = JSON.parse(accountStorage.getItem('qr--wrap') ?? 'false');
548 wrap.addEventListener('click', () => {549 wrap.addEventListener('click', () => {
549 localStorage.setItem('qr--wrap', JSON.stringify(wrap.checked));550 accountStorage.setItem('qr--wrap', JSON.stringify(wrap.checked));
550 updateWrap();551 updateWrap();
551 });552 });
552 const updateWrap = () => {553 const updateWrap = () => {
@@ -594,27 +595,27 @@ export class QuickReply {
594 };595 };
595 /**@type {HTMLInputElement}*/596 /**@type {HTMLInputElement}*/
596 const tabSize = dom.querySelector('#qr--modal-tabSize');597 const tabSize = dom.querySelector('#qr--modal-tabSize');
597 tabSize.value = JSON.parse(localStorage.getItem('qr--tabSize') ?? '4');598 tabSize.value = JSON.parse(accountStorage.getItem('qr--tabSize') ?? '4');
598 const updateTabSize = () => {599 const updateTabSize = () => {
599 message.style.tabSize = tabSize.value;600 message.style.tabSize = tabSize.value;
600 messageSyntaxInner.style.tabSize = tabSize.value;601 messageSyntaxInner.style.tabSize = tabSize.value;
601 updateScrollDebounced();602 updateScrollDebounced();
602 };603 };
603 tabSize.addEventListener('change', () => {604 tabSize.addEventListener('change', () => {
604 localStorage.setItem('qr--tabSize', JSON.stringify(Number(tabSize.value)));605 accountStorage.setItem('qr--tabSize', JSON.stringify(Number(tabSize.value)));
605 updateTabSize();606 updateTabSize();
606 });607 });
607 /**@type {HTMLInputElement}*/608 /**@type {HTMLInputElement}*/
608 const executeShortcut = dom.querySelector('#qr--modal-executeShortcut');609 const executeShortcut = dom.querySelector('#qr--modal-executeShortcut');
609 executeShortcut.checked = JSON.parse(localStorage.getItem('qr--executeShortcut') ?? 'true');610 executeShortcut.checked = JSON.parse(accountStorage.getItem('qr--executeShortcut') ?? 'true');
610 executeShortcut.addEventListener('click', () => {611 executeShortcut.addEventListener('click', () => {
611 localStorage.setItem('qr--executeShortcut', JSON.stringify(executeShortcut.checked));612 accountStorage.setItem('qr--executeShortcut', JSON.stringify(executeShortcut.checked));
612 });613 });
613 /**@type {HTMLInputElement}*/614 /**@type {HTMLInputElement}*/
614 const syntax = dom.querySelector('#qr--modal-syntax');615 const syntax = dom.querySelector('#qr--modal-syntax');
615 syntax.checked = JSON.parse(localStorage.getItem('qr--syntax') ?? 'true');616 syntax.checked = JSON.parse(accountStorage.getItem('qr--syntax') ?? 'true');
616 syntax.addEventListener('click', () => {617 syntax.addEventListener('click', () => {
617 localStorage.setItem('qr--syntax', JSON.stringify(syntax.checked));618 accountStorage.setItem('qr--syntax', JSON.stringify(syntax.checked));
618 updateSyntaxEnabled();619 updateSyntaxEnabled();
619 });620 });
620 if (navigator.keyboard) {621 if (navigator.keyboard) {
public/scripts/extensions/regex/index.js+3 -2
@@ -10,6 +10,7 @@ import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
10import { download, getFileText, getSortableDelay, uuidv4 } from '../../utils.js';10import { download, getFileText, getSortableDelay, uuidv4 } from '../../utils.js';
11import { regex_placement, runRegexScript, substitute_find_regex } from './engine.js';11import { regex_placement, runRegexScript, substitute_find_regex } from './engine.js';
12import { t } from '../../i18n.js';12import { t } from '../../i18n.js';
13import { accountStorage } from '../../util/AccountStorage.js';
1314
14/**15/**
15 * @typedef {object} RegexScript16 * @typedef {object} RegexScript
@@ -440,8 +441,8 @@ async function checkEmbeddedRegexScripts() {
440 if (avatar && !extension_settings.character_allowed_regex.includes(avatar)) {441 if (avatar && !extension_settings.character_allowed_regex.includes(avatar)) {
441 const checkKey = `AlertRegex_${characters[chid].avatar}`;442 const checkKey = `AlertRegex_${characters[chid].avatar}`;
442443
443 if (!localStorage.getItem(checkKey)) {444 if (!accountStorage.getItem(checkKey)) {
444 localStorage.setItem(checkKey, 'true');445 accountStorage.setItem(checkKey, 'true');
445 const template = await renderExtensionTemplateAsync('regex', 'embeddedScripts', {});446 const template = await renderExtensionTemplateAsync('regex', 'embeddedScripts', {});
446 const result = await callGenericPopup(template, POPUP_TYPE.CONFIRM, '', { okButton: 'Yes' });447 const result = await callGenericPopup(template, POPUP_TYPE.CONFIRM, '', { okButton: 'Yes' });
447448
public/scripts/f-localStorage.js+0 -27
@@ -1,27 +0,0 @@
1////////////////// LOCAL STORAGE HANDLING /////////////////////
2
3export function SaveLocal(target, val) {
4 localStorage.setItem(target, val);
5 console.debug('SaveLocal -- ' + target + ' : ' + val);
6}
7export function LoadLocal(target) {
8 console.debug('LoadLocal -- ' + target);
9 return localStorage.getItem(target);
10
11}
12export function LoadLocalBool(target) {
13 let result = localStorage.getItem(target) === 'true';
14 return result;
15}
16export function CheckLocal() {
17 console.log('----------local storage---------');
18 var i;
19 for (i = 0; i < localStorage.length; i++) {
20 console.log(localStorage.key(i) + ' : ' + localStorage.getItem(localStorage.key(i)));
21 }
22 console.log('------------------------------');
23}
24
25export function ClearLocal() { localStorage.clear(); console.log('Removed All Local Storage'); }
26
27/////////////////////////////////////////////////////////////////////////
public/scripts/group-chats.js+5 -4
@@ -78,6 +78,7 @@ import { FILTER_TYPES, FilterHelper } from './filters.js';
78import { isExternalMediaAllowed } from './chats.js';78import { isExternalMediaAllowed } from './chats.js';
79import { POPUP_TYPE, Popup, callGenericPopup } from './popup.js';79import { POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
80import { t } from './i18n.js';80import { t } from './i18n.js';
81import { accountStorage } from './util/AccountStorage.js';
8182
82export {83export {
83 selected_group,84 selected_group,
@@ -1309,10 +1310,10 @@ function printGroupCandidates() {
1309 formatNavigator: PAGINATION_TEMPLATE,1310 formatNavigator: PAGINATION_TEMPLATE,
1310 showNavigator: true,1311 showNavigator: true,
1311 showSizeChanger: true,1312 showSizeChanger: true,
1312 pageSize: Number(localStorage.getItem(storageKey)) || 5,1313 pageSize: Number(accountStorage.getItem(storageKey)) || 5,
1313 sizeChangerOptions: [5, 10, 25, 50, 100, 200, 500, 1000],1314 sizeChangerOptions: [5, 10, 25, 50, 100, 200, 500, 1000],
1314 afterSizeSelectorChange: function (e) {1315 afterSizeSelectorChange: function (e) {
1315 localStorage.setItem(storageKey, e.target.value);1316 accountStorage.setItem(storageKey, e.target.value);
1316 },1317 },
1317 callback: function (data) {1318 callback: function (data) {
1318 $('#rm_group_add_members').empty();1319 $('#rm_group_add_members').empty();
@@ -1336,10 +1337,10 @@ function printGroupMembers() {
1336 formatNavigator: PAGINATION_TEMPLATE,1337 formatNavigator: PAGINATION_TEMPLATE,
1337 showNavigator: true,1338 showNavigator: true,
1338 showSizeChanger: true,1339 showSizeChanger: true,
1339 pageSize: Number(localStorage.getItem(storageKey)) || 5,1340 pageSize: Number(accountStorage.getItem(storageKey)) || 5,
1340 sizeChangerOptions: [5, 10, 25, 50, 100, 200, 500, 1000],1341 sizeChangerOptions: [5, 10, 25, 50, 100, 200, 500, 1000],
1341 afterSizeSelectorChange: function (e) {1342 afterSizeSelectorChange: function (e) {
1342 localStorage.setItem(storageKey, e.target.value);1343 accountStorage.setItem(storageKey, e.target.value);
1343 },1344 },
1344 callback: function (data) {1345 callback: function (data) {
1345 $('.rm_group_members').empty();1346 $('.rm_group_members').empty();
public/scripts/openai.js+3 -2
@@ -73,6 +73,7 @@ import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js
73import { Popup, POPUP_RESULT } from './popup.js';73import { Popup, POPUP_RESULT } from './popup.js';
74import { t } from './i18n.js';74import { t } from './i18n.js';
75import { ToolManager } from './tool-calling.js';75import { ToolManager } from './tool-calling.js';
76import { accountStorage } from './util/AccountStorage.js';
7677
77export {78export {
78 openai_messages_count,79 openai_messages_count,
@@ -414,7 +415,7 @@ async function validateReverseProxy() {
414 throw err;415 throw err;
415 }416 }
416 const rememberKey = `Proxy_SkipConfirm_${getStringHash(oai_settings.reverse_proxy)}`;417 const rememberKey = `Proxy_SkipConfirm_${getStringHash(oai_settings.reverse_proxy)}`;
417 const skipConfirm = localStorage.getItem(rememberKey) === 'true';418 const skipConfirm = accountStorage.getItem(rememberKey) === 'true';
418419
419 const confirmation = skipConfirm || await Popup.show.confirm(t`Connecting To Proxy`, await renderTemplateAsync('proxyConnectionWarning', { proxyURL: DOMPurify.sanitize(oai_settings.reverse_proxy) }));420 const confirmation = skipConfirm || await Popup.show.confirm(t`Connecting To Proxy`, await renderTemplateAsync('proxyConnectionWarning', { proxyURL: DOMPurify.sanitize(oai_settings.reverse_proxy) }));
420421
@@ -425,7 +426,7 @@ async function validateReverseProxy() {
425 throw new Error('Proxy connection denied.');426 throw new Error('Proxy connection denied.');
426 }427 }
427428
428 localStorage.setItem(rememberKey, String(true));429 accountStorage.setItem(rememberKey, String(true));
429}430}
430431
431/**432/**
public/scripts/personas.js+6 -5
@@ -25,6 +25,7 @@ import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
25import { t } from './i18n.js';25import { t } from './i18n.js';
26import { openWorldInfoEditor, world_names } from './world-info.js';26import { openWorldInfoEditor, world_names } from './world-info.js';
27import { renderTemplateAsync } from './templates.js';27import { renderTemplateAsync } from './templates.js';
28import { accountStorage } from './util/AccountStorage.js';
2829
29let savePersonasPage = 0;30let savePersonasPage = 0;
30const GRID_STORAGE_KEY = 'Personas_GridView';31const GRID_STORAGE_KEY = 'Personas_GridView';
@@ -34,7 +35,7 @@ export let user_avatar = '';
34export const personasFilter = new FilterHelper(debounce(getUserAvatars, debounce_timeout.quick));35export const personasFilter = new FilterHelper(debounce(getUserAvatars, debounce_timeout.quick));
3536
36function switchPersonaGridView() {37function switchPersonaGridView() {
37 const state = localStorage.getItem(GRID_STORAGE_KEY) === 'true';38 const state = accountStorage.getItem(GRID_STORAGE_KEY) === 'true';
38 $('#user_avatar_block').toggleClass('gridView', state);39 $('#user_avatar_block').toggleClass('gridView', state);
39}40}
4041
@@ -182,7 +183,7 @@ export async function getUserAvatars(doRender = true, openPageAt = '') {
182183
183 const storageKey = 'Personas_PerPage';184 const storageKey = 'Personas_PerPage';
184 const listId = '#user_avatar_block';185 const listId = '#user_avatar_block';
185 const perPage = Number(localStorage.getItem(storageKey)) || 5;186 const perPage = Number(accountStorage.getItem(storageKey)) || 5;
186187
187 $('#persona_pagination_container').pagination({188 $('#persona_pagination_container').pagination({
188 dataSource: entities,189 dataSource: entities,
@@ -205,7 +206,7 @@ export async function getUserAvatars(doRender = true, openPageAt = '') {
205 highlightSelectedAvatar();206 highlightSelectedAvatar();
206 },207 },
207 afterSizeSelectorChange: function (e) {208 afterSizeSelectorChange: function (e) {
208 localStorage.setItem(storageKey, e.target.value);209 accountStorage.setItem(storageKey, e.target.value);
209 },210 },
210 afterPaging: function (e) {211 afterPaging: function (e) {
211 savePersonasPage = e;212 savePersonasPage = e;
@@ -1132,8 +1133,8 @@ export function initPersonas() {
1132 saveSettingsDebounced();1133 saveSettingsDebounced();
1133 });1134 });
1134 $('#persona_grid_toggle').on('click', () => {1135 $('#persona_grid_toggle').on('click', () => {
1135 const state = localStorage.getItem(GRID_STORAGE_KEY) === 'true';1136 const state = accountStorage.getItem(GRID_STORAGE_KEY) === 'true';
1136 localStorage.setItem(GRID_STORAGE_KEY, String(!state));1137 accountStorage.setItem(GRID_STORAGE_KEY, String(!state));
1137 switchPersonaGridView();1138 switchPersonaGridView();
1138 });1139 });
11391140
public/scripts/power-user.js+3 -2
@@ -54,6 +54,7 @@ import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCom
54import { POPUP_TYPE, callGenericPopup } from './popup.js';54import { POPUP_TYPE, callGenericPopup } from './popup.js';
55import { loadSystemPrompts } from './sysprompt.js';55import { loadSystemPrompts } from './sysprompt.js';
56import { fuzzySearchCategories } from './filters.js';56import { fuzzySearchCategories } from './filters.js';
57import { accountStorage } from './util/AccountStorage.js';
5758
58export {59export {
59 loadPowerUserSettings,60 loadPowerUserSettings,
@@ -2019,7 +2020,7 @@ export function renderStoryString(params) {
2019 */2020 */
2020function validateStoryString(storyString, params) {2021function validateStoryString(storyString, params) {
2021 /** @type {{hashCache: {[hash: string]: {fieldsWarned: {[key: string]: boolean}}}}} */2022 /** @type {{hashCache: {[hash: string]: {fieldsWarned: {[key: string]: boolean}}}}} */
2022 const cache = JSON.parse(localStorage.getItem(storage_keys.storyStringValidationCache)) ?? { hashCache: {} };2023 const cache = JSON.parse(accountStorage.getItem(storage_keys.storyStringValidationCache)) ?? { hashCache: {} };
20232024
2024 const hash = getStringHash(storyString);2025 const hash = getStringHash(storyString);
20252026
@@ -2056,7 +2057,7 @@ function validateStoryString(storyString, params) {
2056 toastr.warning(`The story string does not contain the following fields, but they would contain content: ${fieldsList}`, 'Story String Validation');2057 toastr.warning(`The story string does not contain the following fields, but they would contain content: ${fieldsList}`, 'Story String Validation');
2057 }2058 }
20582059
2059 localStorage.setItem(storage_keys.storyStringValidationCache, JSON.stringify(cache));2060 accountStorage.setItem(storage_keys.storyStringValidationCache, JSON.stringify(cache));
2060}2061}
20612062
20622063
public/scripts/slash-commands.js+3 -2
@@ -75,6 +75,7 @@ import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCom
75import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js';75import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js';
76import { SlashCommandExecutionError } from './slash-commands/SlashCommandExecutionError.js';76import { SlashCommandExecutionError } from './slash-commands/SlashCommandExecutionError.js';
77import { slashCommandReturnHelper } from './slash-commands/SlashCommandReturnHelper.js';77import { slashCommandReturnHelper } from './slash-commands/SlashCommandReturnHelper.js';
78import { accountStorage } from './util/AccountStorage.js';
78export {79export {
79 executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand,80 executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand,
80};81};
@@ -3606,9 +3607,9 @@ export async function sendMessageAs(args, text) {
36063607
3607 if (!name) {3608 if (!name) {
3608 const namelessWarningKey = 'sendAsNamelessWarningShown';3609 const namelessWarningKey = 'sendAsNamelessWarningShown';
3609 if (localStorage.getItem(namelessWarningKey) !== 'true') {3610 if (accountStorage.getItem(namelessWarningKey) !== 'true') {
3610 toastr.warning('To avoid confusion, please use /sendas name="Character Name"', 'Name defaulted to {{char}}', { timeOut: 10000 });3611 toastr.warning('To avoid confusion, please use /sendas name="Character Name"', 'Name defaulted to {{char}}', { timeOut: 10000 });
3611 localStorage.setItem(namelessWarningKey, 'true');3612 accountStorage.setItem(namelessWarningKey, 'true');
3612 }3613 }
3613 name = name2;3614 name = name2;
3614 }3615 }
public/scripts/st-context.js+2 -0
@@ -68,12 +68,14 @@ import { tag_map, tags } from './tags.js';
68import { textgenerationwebui_settings } from './textgen-settings.js';68import { textgenerationwebui_settings } from './textgen-settings.js';
69import { tokenizers, getTextTokens, getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js';69import { tokenizers, getTextTokens, getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js';
70import { ToolManager } from './tool-calling.js';70import { ToolManager } from './tool-calling.js';
71import { accountStorage } from './util/AccountStorage.js';
71import { timestampToMoment, uuidv4 } from './utils.js';72import { timestampToMoment, uuidv4 } from './utils.js';
72import { getGlobalVariable, getLocalVariable, setGlobalVariable, setLocalVariable } from './variables.js';73import { getGlobalVariable, getLocalVariable, setGlobalVariable, setLocalVariable } from './variables.js';
73import { convertCharacterBook, loadWorldInfo, saveWorldInfo, updateWorldInfoList } from './world-info.js';74import { convertCharacterBook, loadWorldInfo, saveWorldInfo, updateWorldInfoList } from './world-info.js';
7475
75export function getContext() {76export function getContext() {
76 return {77 return {
78 accountStorage,
77 chat,79 chat,
78 characters,80 characters,
79 groups,81 groups,
public/scripts/textgen-models.js+4 -3
@@ -6,6 +6,7 @@ import { tokenizers } from './tokenizers.js';
6import { renderTemplateAsync } from './templates.js';6import { renderTemplateAsync } from './templates.js';
7import { POPUP_TYPE, callGenericPopup } from './popup.js';7import { POPUP_TYPE, callGenericPopup } from './popup.js';
8import { t } from './i18n.js';8import { t } from './i18n.js';
9import { accountStorage } from './util/AccountStorage.js';
910
10let mancerModels = [];11let mancerModels = [];
11let togetherModels = [];12let togetherModels = [];
@@ -336,7 +337,7 @@ export async function loadFeatherlessModels(data) {
336 populateClassSelection(data);337 populateClassSelection(data);
337338
338 // Retrieve the stored number of items per page or default to 10339 // Retrieve the stored number of items per page or default to 10
339 const perPage = Number(localStorage.getItem(storageKey)) || 10;340 const perPage = Number(accountStorage.getItem(storageKey)) || 10;
340341
341 // Initialize pagination342 // Initialize pagination
342 applyFiltersAndSort();343 applyFiltersAndSort();
@@ -412,7 +413,7 @@ export async function loadFeatherlessModels(data) {
412 },413 },
413 afterSizeSelectorChange: function (e) {414 afterSizeSelectorChange: function (e) {
414 const newPerPage = e.target.value;415 const newPerPage = e.target.value;
415 localStorage.setItem('Models_PerPage', newPerPage);416 accountStorage.setItem(storageKey, newPerPage);
416 setupPagination(models, Number(newPerPage), featherlessCurrentPage); // Use the stored current page number417 setupPagination(models, Number(newPerPage), featherlessCurrentPage); // Use the stored current page number
417 },418 },
418 });419 });
@@ -513,7 +514,7 @@ export async function loadFeatherlessModels(data) {
513 const currentModelIndex = filteredModels.findIndex(x => x.id === textgen_settings.featherless_model);514 const currentModelIndex = filteredModels.findIndex(x => x.id === textgen_settings.featherless_model);
514 featherlessCurrentPage = currentModelIndex >= 0 ? (currentModelIndex / perPage) + 1 : 1;515 featherlessCurrentPage = currentModelIndex >= 0 ? (currentModelIndex / perPage) + 1 : 1;
515516
516 setupPagination(filteredModels, Number(localStorage.getItem(storageKey)) || perPage, featherlessCurrentPage);517 setupPagination(filteredModels, Number(accountStorage.getItem(storageKey)) || perPage, featherlessCurrentPage);
517 }518 }
518519
519 // Required to keep the /model command function520 // Required to keep the /model command function
public/scripts/user.js+8 -0
@@ -44,6 +44,14 @@ export function isAdmin() {
44}44}
4545
46/**46/**
47 * Gets the handle string of the current user.
48 * @returns {string} User handle
49 */
50export function getCurrentUserHandle() {
51 return currentUser?.handle || 'default-user';
52}
53
54/**
47 * Get the current user.55 * Get the current user.
48 * @returns {Promise<void>}56 * @returns {Promise<void>}
49 */57 */
public/scripts/util/AccountStorage.js+137 -0
@@ -0,0 +1,137 @@
1import { saveSettingsDebounced } from '../../script.js';
2
3const MIGRATED_MARKER = '__migrated';
4const MIGRATABLE_KEYS = [
5 /^AlertRegex_/,
6 /^AlertWI_/,
7 /^Assets_SkipConfirm_/,
8 /^Characters_PerPage$/,
9 /^DataBank_sortField$/,
10 /^DataBank_sortOrder$/,
11 /^extension_update_nag$/,
12 /^extensions_sortByName$/,
13 /^FeatherlessModels_PerPage$/,
14 /^GroupMembers_PerPage$/,
15 /^GroupCandidates_PerPage$/,
16 /^LNavLockOn$/,
17 /^LNavOpened$/,
18 /^mediaWarningShown:/,
19 /^NavLockOn$/,
20 /^NavOpened$/,
21 /^Personas_PerPage$/,
22 /^Personas_GridView$/,
23 /^Proxy_SkipConfirm_/,
24 /^qr--executeShortcut$/,
25 /^qr--syntax$/,
26 /^qr--tabSize$/,
27 /^qr--wrap$/,
28 /^RegenerateWithCtrlEnter$/,
29 /^SelectedNavTab$/,
30 /^sendAsNamelessWarningShown$/,
31 /^StoryStringValidationCache$/,
32 /^WINavOpened$/,
33 /^WI_PerPage$/,
34 /^world_info_sort_order$/,
35];
36
37/**
38 * Provides access to account storage of arbitrary key-value pairs.
39 */
40class AccountStorage {
41 /**
42 * @type {Record<string, string>} Storage state
43 */
44 #state = {};
45
46 /**
47 * @type {boolean} If the storage was initialized
48 */
49 #ready = false;
50
51 #migrateLocalStorage() {
52 for (let i = 0; i < globalThis.localStorage.length; i++) {
53 const key = globalThis.localStorage.key(i);
54 const value = globalThis.localStorage.getItem(key);
55
56 if (MIGRATABLE_KEYS.some(k => k.test(key))) {
57 this.#state[key] = value;
58 globalThis.localStorage.removeItem(key);
59 }
60 }
61 }
62
63 /**
64 * Initialize the account storage.
65 * @param {Object} state Initial state
66 */
67 init(state) {
68 if (state && typeof state === 'object') {
69 this.#state = Object.assign(this.#state, state);
70 }
71
72 if (!Object.hasOwn(this.#state, MIGRATED_MARKER)) {
73 this.#migrateLocalStorage();
74 this.#state[MIGRATED_MARKER] = '1';
75 saveSettingsDebounced();
76 }
77
78 this.#ready = true;
79 }
80
81 /**
82 * Get the value of a key in account storage.
83 * @param {string} key Key to get
84 * @returns {string|null} Value of the key
85 */
86 getItem(key) {
87 if (!this.#ready) {
88 console.warn(`AccountStorage not ready (trying to read from ${key})`);
89 }
90
91 return Object.hasOwn(this.#state, key) ? String(this.#state[key]) : null;
92 }
93
94 /**
95 * Set a key in account storage.
96 * @param {string} key Key to set
97 * @param {string} value Value to set
98 */
99 setItem(key, value) {
100 if (!this.#ready) {
101 console.warn(`AccountStorage not ready (trying to write to ${key})`);
102 }
103
104 this.#state[key] = String(value);
105 saveSettingsDebounced();
106 }
107
108 /**
109 * Remove a key from account storage.
110 * @param {string} key Key to remove
111 */
112 removeItem(key) {
113 if (!this.#ready) {
114 console.warn(`AccountStorage not ready (trying to remove ${key})`);
115 }
116
117 if (!Object.hasOwn(this.#state, key)) {
118 return;
119 }
120
121 delete this.#state[key];
122 saveSettingsDebounced();
123 }
124
125 /**
126 * Gets a snapshot of the storage state.
127 * @returns {Record<string, string>} A deep clone of the storage state
128 */
129 getState() {
130 return structuredClone(this.#state);
131 }
132}
133
134/**
135 * Account storage instance.
136 */
137export const accountStorage = new AccountStorage();
public/scripts/world-info.js+9 -8
@@ -21,6 +21,7 @@ import { callGenericPopup, Popup, POPUP_TYPE } from './popup.js';
21import { StructuredCloneMap } from './util/StructuredCloneMap.js';21import { StructuredCloneMap } from './util/StructuredCloneMap.js';
22import { renderTemplateAsync } from './templates.js';22import { renderTemplateAsync } from './templates.js';
23import { t } from './i18n.js';23import { t } from './i18n.js';
24import { accountStorage } from './util/AccountStorage.js';
2425
25export const world_info_insertion_strategy = {26export const world_info_insertion_strategy = {
26 evenly: 0,27 evenly: 0,
@@ -872,7 +873,7 @@ export function setWorldInfoSettings(settings, data) {
872 $('#world_editor_select').append(`<option value='${i}'>${item}</option>`);873 $('#world_editor_select').append(`<option value='${i}'>${item}</option>`);
873 });874 });
874875
875 $('#world_info_sort_order').val(localStorage.getItem(SORT_ORDER_KEY) || '0');876 $('#world_info_sort_order').val(accountStorage.getItem(SORT_ORDER_KEY) || '0');
876 $('#world_info').trigger('change');877 $('#world_info').trigger('change');
877 $('#world_editor_select').trigger('change');878 $('#world_editor_select').trigger('change');
878879
@@ -1947,13 +1948,13 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl
1947 if (typeof navigation === 'number' && Number(navigation) >= 0) {1948 if (typeof navigation === 'number' && Number(navigation) >= 0) {
1948 const data = getDataArray();1949 const data = getDataArray();
1949 const uidIndex = data.findIndex(x => x.uid === navigation);1950 const uidIndex = data.findIndex(x => x.uid === navigation);
1950 const perPage = Number(localStorage.getItem(storageKey)) || perPageDefault;1951 const perPage = Number(accountStorage.getItem(storageKey)) || perPageDefault;
1951 startPage = Math.floor(uidIndex / perPage) + 1;1952 startPage = Math.floor(uidIndex / perPage) + 1;
1952 }1953 }
19531954
1954 $('#world_info_pagination').pagination({1955 $('#world_info_pagination').pagination({
1955 dataSource: getDataArray,1956 dataSource: getDataArray,
1956 pageSize: Number(localStorage.getItem(storageKey)) || perPageDefault,1957 pageSize: Number(accountStorage.getItem(storageKey)) || perPageDefault,
1957 sizeChangerOptions: [10, 25, 50, 100, 500, 1000],1958 sizeChangerOptions: [10, 25, 50, 100, 500, 1000],
1958 showSizeChanger: true,1959 showSizeChanger: true,
1959 pageRange: 1,1960 pageRange: 1,
@@ -1983,7 +1984,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl
1983 worldEntriesList.append(blocks);1984 worldEntriesList.append(blocks);
1984 },1985 },
1985 afterSizeSelectorChange: function (e) {1986 afterSizeSelectorChange: function (e) {
1986 localStorage.setItem(storageKey, e.target.value);1987 accountStorage.setItem(storageKey, e.target.value);
1987 },1988 },
1988 afterPaging: function () {1989 afterPaging: function () {
1989 $('#world_popup_entries_list textarea[name="comment"]').each(function () {1990 $('#world_popup_entries_list textarea[name="comment"]').each(function () {
@@ -2188,7 +2189,7 @@ function verifyWorldInfoSearchSortRule() {
2188 // If search got cleared, we make sure to hide the option and go back to the one before2189 // If search got cleared, we make sure to hide the option and go back to the one before
2189 if (!searchTerm && !isHidden) {2190 if (!searchTerm && !isHidden) {
2190 searchOption.attr('hidden', '');2191 searchOption.attr('hidden', '');
2191 selector.val(localStorage.getItem(SORT_ORDER_KEY) || '0');2192 selector.val(accountStorage.getItem(SORT_ORDER_KEY) || '0');
2192 }2193 }
2193}2194}
21942195
@@ -4753,8 +4754,8 @@ export function checkEmbeddedWorld(chid) {
4753 // Only show the alert once per character4754 // Only show the alert once per character
4754 const checkKey = `AlertWI_${characters[chid].avatar}`;4755 const checkKey = `AlertWI_${characters[chid].avatar}`;
4755 const worldName = characters[chid]?.data?.extensions?.world;4756 const worldName = characters[chid]?.data?.extensions?.world;
4756 if (!localStorage.getItem(checkKey) && (!worldName || !world_names.includes(worldName))) {4757 if (!accountStorage.getItem(checkKey) && (!worldName || !world_names.includes(worldName))) {
4757 localStorage.setItem(checkKey, 'true');4758 accountStorage.setItem(checkKey, 'true');
47584759
4759 if (power_user.world_import_dialog) {4760 if (power_user.world_import_dialog) {
4760 const html = `<h3>This character has an embedded World/Lorebook.</h3>4761 const html = `<h3>This character has an embedded World/Lorebook.</h3>
@@ -5198,7 +5199,7 @@ jQuery(() => {
5198 $('#world_info_sort_order').on('change', function () {5199 $('#world_info_sort_order').on('change', function () {
5199 const value = String($(this).find(':selected').val());5200 const value = String($(this).find(':selected').val());
5200 // Save sort order, but do not save search sorting, as this is a temporary sorting option5201 // Save sort order, but do not save search sorting, as this is a temporary sorting option
5201 if (value !== 'search') localStorage.setItem(SORT_ORDER_KEY, value);5202 if (value !== 'search') accountStorage.setItem(SORT_ORDER_KEY, value);
5202 updateEditor(navigation_option.none);5203 updateEditor(navigation_option.none);
5203 });5204 });
52045205