Merge branch 'staging' into hidden-reasoning-tracking

This commit is contained in:
Cohee
2025-02-12 20:00:52 +02:00
18 changed files with 253 additions and 108 deletions

View File

@ -270,6 +270,7 @@ import { initBulkEdit } from './scripts/bulk-edit.js';
import { deriveTemplatesFromChatTemplate } from './scripts/chat-templates.js';
import { getContext } from './scripts/st-context.js';
import { extractReasoningFromData, initReasoning, PromptReasoning, ReasoningHandler, removeReasoningFromString, updateReasoningUI } from './scripts/reasoning.js';
import { accountStorage } from './scripts/util/AccountStorage.js';
// API OBJECT FOR EXTERNAL WIRING
globalThis.SillyTavern = {
@ -419,7 +420,7 @@ DOMPurify.addHook('uponSanitizeElement', (node, _, config) => {
const entityId = getCurrentEntityId();
const warningShownKey = `mediaWarningShown:${entityId}`;
if (localStorage.getItem(warningShownKey) === null) {
if (accountStorage.getItem(warningShownKey) === null) {
const warningToast = toastr.warning(
t`Use the 'Ext. Media' button to allow it. Click on this message to dismiss.`,
t`External media has been blocked`,
@ -430,7 +431,7 @@ DOMPurify.addHook('uponSanitizeElement', (node, _, config) => {
},
);
localStorage.setItem(warningShownKey, 'true');
accountStorage.setItem(warningShownKey, 'true');
}
}
});
@ -1490,7 +1491,7 @@ export async function printCharacters(fullRefresh = false) {
$('#rm_print_characters_pagination').pagination({
dataSource: entities,
pageSize: Number(localStorage.getItem(storageKey)) || per_page_default,
pageSize: Number(accountStorage.getItem(storageKey)) || per_page_default,
sizeChangerOptions: [10, 25, 50, 100, 250, 500, 1000],
pageRange: 1,
pageNumber: saveCharactersPage || 1,
@ -1534,7 +1535,7 @@ export async function printCharacters(fullRefresh = false) {
eventSource.emit(event_types.CHARACTER_PAGE_LOADED);
},
afterSizeSelectorChange: function (e) {
localStorage.setItem(storageKey, e.target.value);
accountStorage.setItem(storageKey, e.target.value);
},
afterPaging: function (e) {
saveCharactersPage = e;
@ -6839,10 +6840,11 @@ export async function getSettings() {
$('#your_name').val(name1);
}
accountStorage.init(settings?.accountStorage);
await setUserControls(data.enable_accounts);
// Allow subscribers to mutate settings
eventSource.emit(event_types.SETTINGS_LOADED_BEFORE, settings);
await eventSource.emit(event_types.SETTINGS_LOADED_BEFORE, settings);
//Load KoboldAI settings
koboldai_setting_names = data.koboldai_setting_names;
@ -7010,7 +7012,8 @@ function selectKoboldGuiPreset() {
export async function saveSettings(loopCounter = 0) {
if (!settingsReady) {
console.warn('Settings not ready, aborting save');
console.warn('Settings not ready, scheduling another save');
saveSettingsDebounced();
return;
}
@ -7031,6 +7034,7 @@ export async function saveSettings(loopCounter = 0) {
url: '/api/settings/save',
data: JSON.stringify({
firstRun: firstRun,
accountStorage: accountStorage.getState(),
currentVersion: currentVersion,
username: name1,
active_character: active_character,
@ -7486,7 +7490,7 @@ export function select_rm_info(type, charId, previousCharId = null) {
}
try {
const perPage = Number(localStorage.getItem('Characters_PerPage')) || per_page_default;
const perPage = Number(accountStorage.getItem('Characters_PerPage')) || per_page_default;
const page = Math.floor(charIndex / perPage) + 1;
const selector = `#rm_print_characters_block [title*="${avatarFileName}"]`;
$('#rm_print_characters_pagination').pagination('go', page);
@ -7518,7 +7522,7 @@ export function select_rm_info(type, charId, previousCharId = null) {
return;
}
const perPage = Number(localStorage.getItem('Characters_PerPage')) || per_page_default;
const perPage = Number(accountStorage.getItem('Characters_PerPage')) || per_page_default;
const page = Math.floor(charIndex / perPage) + 1;
$('#rm_print_characters_pagination').pagination('go', page);
const selector = `#rm_print_characters_block [grid="${charId}"]`;
@ -9384,6 +9388,9 @@ export async function deleteCharacter(characterKey, { deleteChats = true } = {})
continue;
}
accountStorage.removeItem(`AlertWI_${character.avatar}`);
accountStorage.removeItem(`AlertRegex_${character.avatar}`);
accountStorage.removeItem(`mediaWarningShown:${character.avatar}`);
delete tag_map[character.avatar];
select_rm_info('char_delete', character.name);
@ -9586,8 +9593,8 @@ function addDebugFunctions() {
});
registerDebugFunction('toggleRegenerateWarning', 'Toggle Ctrl+Enter regeneration confirmation', 'Toggle the warning when regenerating a message with a Ctrl+Enter hotkey.', () => {
localStorage.setItem('RegenerateWithCtrlEnter', localStorage.getItem('RegenerateWithCtrlEnter') === 'true' ? 'false' : 'true');
toastr.info('Regenerate warning is now ' + (localStorage.getItem('RegenerateWithCtrlEnter') === 'true' ? 'disabled' : 'enabled'));
accountStorage.setItem('RegenerateWithCtrlEnter', accountStorage.getItem('RegenerateWithCtrlEnter') === 'true' ? 'false' : 'true');
toastr.info('Regenerate warning is now ' + (accountStorage.getItem('RegenerateWithCtrlEnter') === 'true' ? 'disabled' : 'enabled'));
});
registerDebugFunction('copySetup', 'Copy ST setup to clipboard [WIP]', 'Useful data when reporting bugs', async () => {

View File

@ -27,7 +27,6 @@ import {
send_on_enter_options,
} from './power-user.js';
import { LoadLocal, SaveLocal, LoadLocalBool } from './f-localStorage.js';
import { selected_group, is_group_generating, openGroupById } from './group-chats.js';
import { getTagKeyForEntity, applyTagsOnCharacterSelect } from './tags.js';
import {
@ -41,6 +40,8 @@ import { textgen_types, textgenerationwebui_settings as textgen_settings, getTex
import { debounce_timeout } from './constants.js';
import { Popup } from './popup.js';
import { accountStorage } from './util/AccountStorage.js';
import { getCurrentUserHandle } from './user.js';
var RPanelPin = document.getElementById('rm_button_panel_pin');
var LPanelPin = document.getElementById('lm_button_panel_pin');
@ -409,32 +410,34 @@ function RA_autoconnect(PrevApi) {
function OpenNavPanels() {
if (!isMobile()) {
//auto-open R nav if locked and previously open
if (LoadLocalBool('NavLockOn') == true && LoadLocalBool('NavOpened') == true) {
if (accountStorage.getItem('NavLockOn') == 'true' && accountStorage.getItem('NavOpened') == 'true') {
//console.log("RA -- clicking right nav to open");
$('#rightNavDrawerIcon').click();
}
//auto-open L nav if locked and previously open
if (LoadLocalBool('LNavLockOn') == true && LoadLocalBool('LNavOpened') == true) {
if (accountStorage.getItem('LNavLockOn') == 'true' && accountStorage.getItem('LNavOpened') == 'true') {
console.debug('RA -- clicking left nav to open');
$('#leftNavDrawerIcon').click();
}
//auto-open WI if locked and previously open
if (LoadLocalBool('WINavLockOn') == true && LoadLocalBool('WINavOpened') == true) {
if (accountStorage.getItem('WINavLockOn') == 'true' && accountStorage.getItem('WINavOpened') == 'true') {
console.debug('RA -- clicking WI to open');
$('#WIDrawerIcon').click();
}
}
}
const getUserInputKey = () => getCurrentUserHandle() + '_userInput';
function restoreUserInput() {
if (!power_user.restore_user_input) {
console.debug('restoreUserInput disabled');
return;
}
const userInput = LoadLocal('userInput');
const userInput = localStorage.getItem(getUserInputKey());
if (userInput) {
$('#send_textarea').val(userInput)[0].dispatchEvent(new Event('input', { bubbles: true }));
}
@ -442,7 +445,8 @@ function restoreUserInput() {
function saveUserInput() {
const userInput = String($('#send_textarea').val());
SaveLocal('userInput', userInput);
localStorage.setItem(getUserInputKey(), userInput);
console.debug('User Input -- ', userInput);
}
const saveUserInputDebounced = debounce(saveUserInput);
@ -739,7 +743,7 @@ export function initRossMods() {
//toggle pin class when lock toggle clicked
$(RPanelPin).on('click', function () {
SaveLocal('NavLockOn', $(RPanelPin).prop('checked'));
accountStorage.setItem('NavLockOn', $(RPanelPin).prop('checked'));
if ($(RPanelPin).prop('checked') == true) {
//console.log('adding pin class to right nav');
$(RightNavPanel).addClass('pinnedOpen');
@ -757,7 +761,7 @@ export function initRossMods() {
}
});
$(LPanelPin).on('click', function () {
SaveLocal('LNavLockOn', $(LPanelPin).prop('checked'));
accountStorage.setItem('LNavLockOn', $(LPanelPin).prop('checked'));
if ($(LPanelPin).prop('checked') == true) {
//console.log('adding pin class to Left nav');
$(LeftNavPanel).addClass('pinnedOpen');
@ -776,7 +780,7 @@ export function initRossMods() {
});
$(WIPanelPin).on('click', function () {
SaveLocal('WINavLockOn', $(WIPanelPin).prop('checked'));
accountStorage.setItem('WINavLockOn', $(WIPanelPin).prop('checked'));
if ($(WIPanelPin).prop('checked') == true) {
console.debug('adding pin class to WI');
$(WorldInfo).addClass('pinnedOpen');
@ -796,8 +800,8 @@ export function initRossMods() {
});
// read the state of right Nav Lock and apply to rightnav classlist
$(RPanelPin).prop('checked', LoadLocalBool('NavLockOn'));
if (LoadLocalBool('NavLockOn') == true) {
$(RPanelPin).prop('checked', accountStorage.getItem('NavLockOn') == 'true');
if (accountStorage.getItem('NavLockOn') == 'true') {
//console.log('setting pin class via local var');
$(RightNavPanel).addClass('pinnedOpen');
$(RightNavDrawerIcon).addClass('drawerPinnedOpen');
@ -808,8 +812,8 @@ export function initRossMods() {
$(RightNavDrawerIcon).addClass('drawerPinnedOpen');
}
// read the state of left Nav Lock and apply to leftnav classlist
$(LPanelPin).prop('checked', LoadLocalBool('LNavLockOn'));
if (LoadLocalBool('LNavLockOn') == true) {
$(LPanelPin).prop('checked', accountStorage.getItem('LNavLockOn') === 'true');
if (accountStorage.getItem('LNavLockOn') == 'true') {
//console.log('setting pin class via local var');
$(LeftNavPanel).addClass('pinnedOpen');
$(LeftNavDrawerIcon).addClass('drawerPinnedOpen');
@ -821,8 +825,8 @@ export function initRossMods() {
}
// read the state of left Nav Lock and apply to leftnav classlist
$(WIPanelPin).prop('checked', LoadLocalBool('WINavLockOn'));
if (LoadLocalBool('WINavLockOn') == true) {
$(WIPanelPin).prop('checked', accountStorage.getItem('WINavLockOn') === 'true');
if (accountStorage.getItem('WINavLockOn') == 'true') {
//console.log('setting pin class via local var');
$(WorldInfo).addClass('pinnedOpen');
$(WIDrawerIcon).addClass('drawerPinnedOpen');
@ -837,22 +841,22 @@ export function initRossMods() {
//save state of Right nav being open or closed
$('#rightNavDrawerIcon').on('click', function () {
if (!$('#rightNavDrawerIcon').hasClass('openIcon')) {
SaveLocal('NavOpened', 'true');
} else { SaveLocal('NavOpened', 'false'); }
accountStorage.setItem('NavOpened', 'true');
} else { accountStorage.setItem('NavOpened', 'false'); }
});
//save state of Left nav being open or closed
$('#leftNavDrawerIcon').on('click', function () {
if (!$('#leftNavDrawerIcon').hasClass('openIcon')) {
SaveLocal('LNavOpened', 'true');
} else { SaveLocal('LNavOpened', 'false'); }
accountStorage.setItem('LNavOpened', 'true');
} else { accountStorage.setItem('LNavOpened', 'false'); }
});
//save state of Left nav being open or closed
$('#WorldInfo').on('click', function () {
if (!$('#WorldInfo').hasClass('openIcon')) {
SaveLocal('WINavOpened', 'true');
} else { SaveLocal('WINavOpened', 'false'); }
accountStorage.setItem('WINavOpened', 'true');
} else { accountStorage.setItem('WINavOpened', 'false'); }
});
var chatbarInFocus = false;
@ -868,8 +872,8 @@ export function initRossMods() {
OpenNavPanels();
}, 300);
$(SelectedCharacterTab).click(function () { SaveLocal('SelectedNavTab', 'rm_button_selected_ch'); });
$('#rm_button_characters').click(function () { SaveLocal('SelectedNavTab', 'rm_button_characters'); });
$(SelectedCharacterTab).click(function () { accountStorage.setItem('SelectedNavTab', 'rm_button_selected_ch'); });
$('#rm_button_characters').click(function () { accountStorage.setItem('SelectedNavTab', 'rm_button_characters'); });
// when a char is selected from the list, save them as the auto-load character for next page load
@ -1077,7 +1081,7 @@ export function initRossMods() {
}
else if (is_send_press == false) {
const skipConfirmKey = 'RegenerateWithCtrlEnter';
const skipConfirm = LoadLocalBool(skipConfirmKey);
const skipConfirm = accountStorage.getItem(skipConfirmKey) === 'true';
function doRegenerate() {
console.debug('Regenerating with Ctrl+Enter');
$('#option_regenerate').trigger('click');
@ -1097,7 +1101,7 @@ export function initRossMods() {
return;
}
SaveLocal(skipConfirmKey, regenerateWithCtrlEnter);
accountStorage.setItem(skipConfirmKey, String(regenerateWithCtrlEnter));
doRegenerate();
}
return;

View File

@ -45,6 +45,7 @@ import { DragAndDropHandler } from './dragdrop.js';
import { renderTemplateAsync } from './templates.js';
import { t } from './i18n.js';
import { humanizedDateTime } from './RossAscends-mods.js';
import { accountStorage } from './util/AccountStorage.js';
/**
* @typedef {Object} FileAttachment
@ -1078,8 +1079,8 @@ async function openAttachmentManager() {
renderAttachments();
});
let sortField = localStorage.getItem('DataBank_sortField') || 'created';
let sortOrder = localStorage.getItem('DataBank_sortOrder') || 'desc';
let sortField = accountStorage.getItem('DataBank_sortField') || 'created';
let sortOrder = accountStorage.getItem('DataBank_sortOrder') || 'desc';
let filterString = '';
const template = $(await renderExtensionTemplateAsync('attachments', 'manager', {}));
@ -1095,8 +1096,8 @@ async function openAttachmentManager() {
sortField = this.selectedOptions[0].dataset.sortField;
sortOrder = this.selectedOptions[0].dataset.sortOrder;
localStorage.setItem('DataBank_sortField', sortField);
localStorage.setItem('DataBank_sortOrder', sortOrder);
accountStorage.setItem('DataBank_sortField', sortField);
accountStorage.setItem('DataBank_sortOrder', sortOrder);
renderAttachments();
});
function handleBulkAction(action) {

View File

@ -9,6 +9,7 @@ import { getContext } from './st-context.js';
import { isAdmin } from './user.js';
import { t } from './i18n.js';
import { debounce_timeout } from './constants.js';
import { accountStorage } from './util/AccountStorage.js';
export {
getContext,
@ -714,7 +715,7 @@ async function showExtensionsDetails() {
htmlExternal.append(htmlLoading);
const sortOrderKey = 'extensions_sortByName';
const sortByName = localStorage.getItem(sortOrderKey) === 'true';
const sortByName = accountStorage.getItem(sortOrderKey) === 'true';
const sortFn = sortByName ? sortManifestsByName : sortManifestsByOrder;
const extensions = Object.entries(manifests).sort((a, b) => sortFn(a[1], b[1])).map(getExtensionData);
@ -745,7 +746,7 @@ async function showExtensionsDetails() {
text: sortByName ? t`Sort: Display Name` : t`Sort: Loading Order`,
action: async () => {
abortController.abort();
localStorage.setItem(sortOrderKey, sortByName ? 'false' : 'true');
accountStorage.setItem(sortOrderKey, sortByName ? 'false' : 'true');
await showExtensionsDetails();
},
};
@ -1153,11 +1154,11 @@ async function checkForExtensionUpdates(force) {
const currentDate = new Date().toDateString();
// Don't nag more than once a day
if (localStorage.getItem(STORAGE_NAG_KEY) === currentDate) {
if (accountStorage.getItem(STORAGE_NAG_KEY) === currentDate) {
return;
}
localStorage.setItem(STORAGE_NAG_KEY, currentDate);
accountStorage.setItem(STORAGE_NAG_KEY, currentDate);
}
const isCurrentUserAdmin = isAdmin();

View File

@ -8,6 +8,7 @@ import { getRequestHeaders, processDroppedFiles, eventSource, event_types } from
import { deleteExtension, extensionNames, getContext, installExtension, renderExtensionTemplateAsync } from '../../extensions.js';
import { POPUP_TYPE, Popup, callGenericPopup } from '../../popup.js';
import { executeSlashCommands } from '../../slash-commands.js';
import { accountStorage } from '../../util/AccountStorage.js';
import { flashHighlight, getStringHash, isValidUrl } from '../../utils.js';
export { MODULE_NAME };
@ -432,14 +433,14 @@ jQuery(async () => {
connectButton.on('click', async function () {
const url = DOMPurify.sanitize(String(assetsJsonUrl.val()));
const rememberKey = `Assets_SkipConfirm_${getStringHash(url)}`;
const skipConfirm = localStorage.getItem(rememberKey) === 'true';
const skipConfirm = accountStorage.getItem(rememberKey) === 'true';
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>`, {
customInputs: [{ id: 'assets-remember', label: 'Don\'t ask again for this URL' }],
onClose: popup => {
if (popup.result) {
const rememberValue = popup.inputResults.get('assets-remember');
localStorage.setItem(rememberKey, String(rememberValue));
accountStorage.setItem(rememberKey, String(rememberValue));
}
},
});

View File

@ -10,6 +10,7 @@ import { SlashCommandExecutor } from '../../../slash-commands/SlashCommandExecut
import { SlashCommandParser } from '../../../slash-commands/SlashCommandParser.js';
import { SlashCommandParserError } from '../../../slash-commands/SlashCommandParserError.js';
import { SlashCommandScope } from '../../../slash-commands/SlashCommandScope.js';
import { accountStorage } from '../../../util/AccountStorage.js';
import { debounce, delay, getSortableDelay, showFontAwesomePicker } from '../../../utils.js';
import { log, quickReplyApi, warn } from '../index.js';
import { QuickReplyContextLink } from './QuickReplyContextLink.js';
@ -544,9 +545,9 @@ export class QuickReply {
this.editorSyntax = messageSyntaxInner;
/**@type {HTMLInputElement}*/
const wrap = dom.querySelector('#qr--modal-wrap');
wrap.checked = JSON.parse(localStorage.getItem('qr--wrap') ?? 'false');
wrap.checked = JSON.parse(accountStorage.getItem('qr--wrap') ?? 'false');
wrap.addEventListener('click', () => {
localStorage.setItem('qr--wrap', JSON.stringify(wrap.checked));
accountStorage.setItem('qr--wrap', JSON.stringify(wrap.checked));
updateWrap();
});
const updateWrap = () => {
@ -594,27 +595,27 @@ export class QuickReply {
};
/**@type {HTMLInputElement}*/
const tabSize = dom.querySelector('#qr--modal-tabSize');
tabSize.value = JSON.parse(localStorage.getItem('qr--tabSize') ?? '4');
tabSize.value = JSON.parse(accountStorage.getItem('qr--tabSize') ?? '4');
const updateTabSize = () => {
message.style.tabSize = tabSize.value;
messageSyntaxInner.style.tabSize = tabSize.value;
updateScrollDebounced();
};
tabSize.addEventListener('change', () => {
localStorage.setItem('qr--tabSize', JSON.stringify(Number(tabSize.value)));
accountStorage.setItem('qr--tabSize', JSON.stringify(Number(tabSize.value)));
updateTabSize();
});
/**@type {HTMLInputElement}*/
const executeShortcut = dom.querySelector('#qr--modal-executeShortcut');
executeShortcut.checked = JSON.parse(localStorage.getItem('qr--executeShortcut') ?? 'true');
executeShortcut.checked = JSON.parse(accountStorage.getItem('qr--executeShortcut') ?? 'true');
executeShortcut.addEventListener('click', () => {
localStorage.setItem('qr--executeShortcut', JSON.stringify(executeShortcut.checked));
accountStorage.setItem('qr--executeShortcut', JSON.stringify(executeShortcut.checked));
});
/**@type {HTMLInputElement}*/
const syntax = dom.querySelector('#qr--modal-syntax');
syntax.checked = JSON.parse(localStorage.getItem('qr--syntax') ?? 'true');
syntax.checked = JSON.parse(accountStorage.getItem('qr--syntax') ?? 'true');
syntax.addEventListener('click', () => {
localStorage.setItem('qr--syntax', JSON.stringify(syntax.checked));
accountStorage.setItem('qr--syntax', JSON.stringify(syntax.checked));
updateSyntaxEnabled();
});
if (navigator.keyboard) {

View File

@ -10,6 +10,7 @@ import { SlashCommandParser } from '../../slash-commands/SlashCommandParser.js';
import { download, getFileText, getSortableDelay, uuidv4 } from '../../utils.js';
import { regex_placement, runRegexScript, substitute_find_regex } from './engine.js';
import { t } from '../../i18n.js';
import { accountStorage } from '../../util/AccountStorage.js';
/**
* @typedef {object} RegexScript
@ -440,8 +441,8 @@ async function checkEmbeddedRegexScripts() {
if (avatar && !extension_settings.character_allowed_regex.includes(avatar)) {
const checkKey = `AlertRegex_${characters[chid].avatar}`;
if (!localStorage.getItem(checkKey)) {
localStorage.setItem(checkKey, 'true');
if (!accountStorage.getItem(checkKey)) {
accountStorage.setItem(checkKey, 'true');
const template = await renderExtensionTemplateAsync('regex', 'embeddedScripts', {});
const result = await callGenericPopup(template, POPUP_TYPE.CONFIRM, '', { okButton: 'Yes' });

View File

@ -1,27 +0,0 @@
////////////////// LOCAL STORAGE HANDLING /////////////////////
export function SaveLocal(target, val) {
localStorage.setItem(target, val);
console.debug('SaveLocal -- ' + target + ' : ' + val);
}
export function LoadLocal(target) {
console.debug('LoadLocal -- ' + target);
return localStorage.getItem(target);
}
export function LoadLocalBool(target) {
let result = localStorage.getItem(target) === 'true';
return result;
}
export function CheckLocal() {
console.log('----------local storage---------');
var i;
for (i = 0; i < localStorage.length; i++) {
console.log(localStorage.key(i) + ' : ' + localStorage.getItem(localStorage.key(i)));
}
console.log('------------------------------');
}
export function ClearLocal() { localStorage.clear(); console.log('Removed All Local Storage'); }
/////////////////////////////////////////////////////////////////////////

View File

@ -78,6 +78,7 @@ import { FILTER_TYPES, FilterHelper } from './filters.js';
import { isExternalMediaAllowed } from './chats.js';
import { POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
import { t } from './i18n.js';
import { accountStorage } from './util/AccountStorage.js';
export {
selected_group,
@ -1309,10 +1310,10 @@ function printGroupCandidates() {
formatNavigator: PAGINATION_TEMPLATE,
showNavigator: true,
showSizeChanger: true,
pageSize: Number(localStorage.getItem(storageKey)) || 5,
pageSize: Number(accountStorage.getItem(storageKey)) || 5,
sizeChangerOptions: [5, 10, 25, 50, 100, 200, 500, 1000],
afterSizeSelectorChange: function (e) {
localStorage.setItem(storageKey, e.target.value);
accountStorage.setItem(storageKey, e.target.value);
},
callback: function (data) {
$('#rm_group_add_members').empty();
@ -1336,10 +1337,10 @@ function printGroupMembers() {
formatNavigator: PAGINATION_TEMPLATE,
showNavigator: true,
showSizeChanger: true,
pageSize: Number(localStorage.getItem(storageKey)) || 5,
pageSize: Number(accountStorage.getItem(storageKey)) || 5,
sizeChangerOptions: [5, 10, 25, 50, 100, 200, 500, 1000],
afterSizeSelectorChange: function (e) {
localStorage.setItem(storageKey, e.target.value);
accountStorage.setItem(storageKey, e.target.value);
},
callback: function (data) {
$('.rm_group_members').empty();

View File

@ -73,6 +73,7 @@ import { SlashCommandEnumValue } from './slash-commands/SlashCommandEnumValue.js
import { Popup, POPUP_RESULT } from './popup.js';
import { t } from './i18n.js';
import { ToolManager } from './tool-calling.js';
import { accountStorage } from './util/AccountStorage.js';
export {
openai_messages_count,
@ -413,7 +414,7 @@ async function validateReverseProxy() {
throw err;
}
const rememberKey = `Proxy_SkipConfirm_${getStringHash(oai_settings.reverse_proxy)}`;
const skipConfirm = localStorage.getItem(rememberKey) === 'true';
const skipConfirm = accountStorage.getItem(rememberKey) === 'true';
const confirmation = skipConfirm || await Popup.show.confirm(t`Connecting To Proxy`, await renderTemplateAsync('proxyConnectionWarning', { proxyURL: DOMPurify.sanitize(oai_settings.reverse_proxy) }));
@ -424,7 +425,7 @@ async function validateReverseProxy() {
throw new Error('Proxy connection denied.');
}
localStorage.setItem(rememberKey, String(true));
accountStorage.setItem(rememberKey, String(true));
}
/**

View File

@ -25,6 +25,7 @@ import { POPUP_RESULT, POPUP_TYPE, Popup, callGenericPopup } from './popup.js';
import { t } from './i18n.js';
import { openWorldInfoEditor, world_names } from './world-info.js';
import { renderTemplateAsync } from './templates.js';
import { accountStorage } from './util/AccountStorage.js';
let savePersonasPage = 0;
const GRID_STORAGE_KEY = 'Personas_GridView';
@ -34,7 +35,7 @@ export let user_avatar = '';
export const personasFilter = new FilterHelper(debounce(getUserAvatars, debounce_timeout.quick));
function switchPersonaGridView() {
const state = localStorage.getItem(GRID_STORAGE_KEY) === 'true';
const state = accountStorage.getItem(GRID_STORAGE_KEY) === 'true';
$('#user_avatar_block').toggleClass('gridView', state);
}
@ -182,7 +183,7 @@ export async function getUserAvatars(doRender = true, openPageAt = '') {
const storageKey = 'Personas_PerPage';
const listId = '#user_avatar_block';
const perPage = Number(localStorage.getItem(storageKey)) || 5;
const perPage = Number(accountStorage.getItem(storageKey)) || 5;
$('#persona_pagination_container').pagination({
dataSource: entities,
@ -205,7 +206,7 @@ export async function getUserAvatars(doRender = true, openPageAt = '') {
highlightSelectedAvatar();
},
afterSizeSelectorChange: function (e) {
localStorage.setItem(storageKey, e.target.value);
accountStorage.setItem(storageKey, e.target.value);
},
afterPaging: function (e) {
savePersonasPage = e;
@ -1132,8 +1133,8 @@ export function initPersonas() {
saveSettingsDebounced();
});
$('#persona_grid_toggle').on('click', () => {
const state = localStorage.getItem(GRID_STORAGE_KEY) === 'true';
localStorage.setItem(GRID_STORAGE_KEY, String(!state));
const state = accountStorage.getItem(GRID_STORAGE_KEY) === 'true';
accountStorage.setItem(GRID_STORAGE_KEY, String(!state));
switchPersonaGridView();
});

View File

@ -54,6 +54,7 @@ import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCom
import { POPUP_TYPE, callGenericPopup } from './popup.js';
import { loadSystemPrompts } from './sysprompt.js';
import { fuzzySearchCategories } from './filters.js';
import { accountStorage } from './util/AccountStorage.js';
export {
loadPowerUserSettings,
@ -2019,7 +2020,7 @@ export function renderStoryString(params) {
*/
function validateStoryString(storyString, params) {
/** @type {{hashCache: {[hash: string]: {fieldsWarned: {[key: string]: boolean}}}}} */
const cache = JSON.parse(localStorage.getItem(storage_keys.storyStringValidationCache)) ?? { hashCache: {} };
const cache = JSON.parse(accountStorage.getItem(storage_keys.storyStringValidationCache)) ?? { hashCache: {} };
const hash = getStringHash(storyString);
@ -2056,7 +2057,7 @@ function validateStoryString(storyString, params) {
toastr.warning(`The story string does not contain the following fields, but they would contain content: ${fieldsList}`, 'Story String Validation');
}
localStorage.setItem(storage_keys.storyStringValidationCache, JSON.stringify(cache));
accountStorage.setItem(storage_keys.storyStringValidationCache, JSON.stringify(cache));
}

View File

@ -75,6 +75,7 @@ import { commonEnumProviders, enumIcons } from './slash-commands/SlashCommandCom
import { SlashCommandBreakController } from './slash-commands/SlashCommandBreakController.js';
import { SlashCommandExecutionError } from './slash-commands/SlashCommandExecutionError.js';
import { slashCommandReturnHelper } from './slash-commands/SlashCommandReturnHelper.js';
import { accountStorage } from './util/AccountStorage.js';
export {
executeSlashCommands, executeSlashCommandsWithOptions, getSlashCommandsHelp, registerSlashCommand,
};
@ -3606,9 +3607,9 @@ export async function sendMessageAs(args, text) {
if (!name) {
const namelessWarningKey = 'sendAsNamelessWarningShown';
if (localStorage.getItem(namelessWarningKey) !== 'true') {
if (accountStorage.getItem(namelessWarningKey) !== 'true') {
toastr.warning('To avoid confusion, please use /sendas name="Character Name"', 'Name defaulted to {{char}}', { timeOut: 10000 });
localStorage.setItem(namelessWarningKey, 'true');
accountStorage.setItem(namelessWarningKey, 'true');
}
name = name2;
}

View File

@ -68,12 +68,14 @@ import { tag_map, tags } from './tags.js';
import { textgenerationwebui_settings } from './textgen-settings.js';
import { tokenizers, getTextTokens, getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js';
import { ToolManager } from './tool-calling.js';
import { accountStorage } from './util/AccountStorage.js';
import { timestampToMoment, uuidv4 } from './utils.js';
import { getGlobalVariable, getLocalVariable, setGlobalVariable, setLocalVariable } from './variables.js';
import { convertCharacterBook, loadWorldInfo, saveWorldInfo, updateWorldInfoList } from './world-info.js';
export function getContext() {
return {
accountStorage,
chat,
characters,
groups,

View File

@ -6,6 +6,7 @@ import { tokenizers } from './tokenizers.js';
import { renderTemplateAsync } from './templates.js';
import { POPUP_TYPE, callGenericPopup } from './popup.js';
import { t } from './i18n.js';
import { accountStorage } from './util/AccountStorage.js';
let mancerModels = [];
let togetherModels = [];
@ -336,7 +337,7 @@ export async function loadFeatherlessModels(data) {
populateClassSelection(data);
// Retrieve the stored number of items per page or default to 10
const perPage = Number(localStorage.getItem(storageKey)) || 10;
const perPage = Number(accountStorage.getItem(storageKey)) || 10;
// Initialize pagination
applyFiltersAndSort();
@ -412,7 +413,7 @@ export async function loadFeatherlessModels(data) {
},
afterSizeSelectorChange: function (e) {
const newPerPage = e.target.value;
localStorage.setItem('Models_PerPage', newPerPage);
accountStorage.setItem(storageKey, newPerPage);
setupPagination(models, Number(newPerPage), featherlessCurrentPage); // Use the stored current page number
},
});
@ -513,7 +514,7 @@ export async function loadFeatherlessModels(data) {
const currentModelIndex = filteredModels.findIndex(x => x.id === textgen_settings.featherless_model);
featherlessCurrentPage = currentModelIndex >= 0 ? (currentModelIndex / perPage) + 1 : 1;
setupPagination(filteredModels, Number(localStorage.getItem(storageKey)) || perPage, featherlessCurrentPage);
setupPagination(filteredModels, Number(accountStorage.getItem(storageKey)) || perPage, featherlessCurrentPage);
}
// Required to keep the /model command function

View File

@ -43,6 +43,14 @@ export function isAdmin() {
return Boolean(currentUser.admin);
}
/**
* Gets the handle string of the current user.
* @returns {string} User handle
*/
export function getCurrentUserHandle() {
return currentUser?.handle || 'default-user';
}
/**
* Get the current user.
* @returns {Promise<void>}

View File

@ -0,0 +1,139 @@
import { saveSettingsDebounced } from '../../script.js';
const MIGRATED_MARKER = '__migrated';
const MIGRATABLE_KEYS = [
/^AlertRegex_/,
/^AlertWI_/,
/^Assets_SkipConfirm_/,
/^Characters_PerPage$/,
/^DataBank_sortField$/,
/^DataBank_sortOrder$/,
/^extension_update_nag$/,
/^extensions_sortByName$/,
/^FeatherlessModels_PerPage$/,
/^GroupMembers_PerPage$/,
/^GroupCandidates_PerPage$/,
/^LNavLockOn$/,
/^LNavOpened$/,
/^mediaWarningShown:/,
/^NavLockOn$/,
/^NavOpened$/,
/^Personas_PerPage$/,
/^Personas_GridView$/,
/^Proxy_SkipConfirm_/,
/^qr--executeShortcut$/,
/^qr--syntax$/,
/^qr--tabSize$/,
/^qr--wrap$/,
/^RegenerateWithCtrlEnter$/,
/^SelectedNavTab$/,
/^sendAsNamelessWarningShown$/,
/^StoryStringValidationCache$/,
/^WINavOpened$/,
/^WI_PerPage$/,
/^world_info_sort_order$/,
];
/**
* Provides access to account storage of arbitrary key-value pairs.
*/
class AccountStorage {
/**
* @type {Record<string, string>} Storage state
*/
#state = {};
/**
* @type {boolean} If the storage was initialized
*/
#ready = false;
#migrateLocalStorage() {
const localStorageKeys = [];
for (let i = 0; i < globalThis.localStorage.length; i++) {
localStorageKeys.push(globalThis.localStorage.key(i));
}
for (const key of localStorageKeys) {
if (MIGRATABLE_KEYS.some(k => k.test(key))) {
const value = globalThis.localStorage.getItem(key);
this.#state[key] = value;
globalThis.localStorage.removeItem(key);
}
}
}
/**
* Initialize the account storage.
* @param {Object} state Initial state
*/
init(state) {
if (state && typeof state === 'object') {
this.#state = Object.assign(this.#state, state);
}
if (!Object.hasOwn(this.#state, MIGRATED_MARKER)) {
this.#migrateLocalStorage();
this.#state[MIGRATED_MARKER] = '1';
saveSettingsDebounced();
}
this.#ready = true;
}
/**
* Get the value of a key in account storage.
* @param {string} key Key to get
* @returns {string|null} Value of the key
*/
getItem(key) {
if (!this.#ready) {
console.warn(`AccountStorage not ready (trying to read from ${key})`);
}
return Object.hasOwn(this.#state, key) ? String(this.#state[key]) : null;
}
/**
* Set a key in account storage.
* @param {string} key Key to set
* @param {string} value Value to set
*/
setItem(key, value) {
if (!this.#ready) {
console.warn(`AccountStorage not ready (trying to write to ${key})`);
}
this.#state[key] = String(value);
saveSettingsDebounced();
}
/**
* Remove a key from account storage.
* @param {string} key Key to remove
*/
removeItem(key) {
if (!this.#ready) {
console.warn(`AccountStorage not ready (trying to remove ${key})`);
}
if (!Object.hasOwn(this.#state, key)) {
return;
}
delete this.#state[key];
saveSettingsDebounced();
}
/**
* Gets a snapshot of the storage state.
* @returns {Record<string, string>} A deep clone of the storage state
*/
getState() {
return structuredClone(this.#state);
}
}
/**
* Account storage instance.
*/
export const accountStorage = new AccountStorage();

View File

@ -21,6 +21,7 @@ import { callGenericPopup, Popup, POPUP_TYPE } from './popup.js';
import { StructuredCloneMap } from './util/StructuredCloneMap.js';
import { renderTemplateAsync } from './templates.js';
import { t } from './i18n.js';
import { accountStorage } from './util/AccountStorage.js';
export const world_info_insertion_strategy = {
evenly: 0,
@ -872,7 +873,7 @@ export function setWorldInfoSettings(settings, data) {
$('#world_editor_select').append(`<option value='${i}'>${item}</option>`);
});
$('#world_info_sort_order').val(localStorage.getItem(SORT_ORDER_KEY) || '0');
$('#world_info_sort_order').val(accountStorage.getItem(SORT_ORDER_KEY) || '0');
$('#world_info').trigger('change');
$('#world_editor_select').trigger('change');
@ -1947,13 +1948,13 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl
if (typeof navigation === 'number' && Number(navigation) >= 0) {
const data = getDataArray();
const uidIndex = data.findIndex(x => x.uid === navigation);
const perPage = Number(localStorage.getItem(storageKey)) || perPageDefault;
const perPage = Number(accountStorage.getItem(storageKey)) || perPageDefault;
startPage = Math.floor(uidIndex / perPage) + 1;
}
$('#world_info_pagination').pagination({
dataSource: getDataArray,
pageSize: Number(localStorage.getItem(storageKey)) || perPageDefault,
pageSize: Number(accountStorage.getItem(storageKey)) || perPageDefault,
sizeChangerOptions: [10, 25, 50, 100, 500, 1000],
showSizeChanger: true,
pageRange: 1,
@ -1983,7 +1984,7 @@ function displayWorldEntries(name, data, navigation = navigation_option.none, fl
worldEntriesList.append(blocks);
},
afterSizeSelectorChange: function (e) {
localStorage.setItem(storageKey, e.target.value);
accountStorage.setItem(storageKey, e.target.value);
},
afterPaging: function () {
$('#world_popup_entries_list textarea[name="comment"]').each(function () {
@ -2188,7 +2189,7 @@ function verifyWorldInfoSearchSortRule() {
// If search got cleared, we make sure to hide the option and go back to the one before
if (!searchTerm && !isHidden) {
searchOption.attr('hidden', '');
selector.val(localStorage.getItem(SORT_ORDER_KEY) || '0');
selector.val(accountStorage.getItem(SORT_ORDER_KEY) || '0');
}
}
@ -4753,8 +4754,8 @@ export function checkEmbeddedWorld(chid) {
// Only show the alert once per character
const checkKey = `AlertWI_${characters[chid].avatar}`;
const worldName = characters[chid]?.data?.extensions?.world;
if (!localStorage.getItem(checkKey) && (!worldName || !world_names.includes(worldName))) {
localStorage.setItem(checkKey, 'true');
if (!accountStorage.getItem(checkKey) && (!worldName || !world_names.includes(worldName))) {
accountStorage.setItem(checkKey, 'true');
if (power_user.world_import_dialog) {
const html = `<h3>This character has an embedded World/Lorebook.</h3>
@ -5198,7 +5199,7 @@ jQuery(() => {
$('#world_info_sort_order').on('change', function () {
const value = String($(this).find(':selected').val());
// Save sort order, but do not save search sorting, as this is a temporary sorting option
if (value !== 'search') localStorage.setItem(SORT_ORDER_KEY, value);
if (value !== 'search') accountStorage.setItem(SORT_ORDER_KEY, value);
updateEditor(navigation_option.none);
});