Merge pull request #3915 from SillyTavern/feat/refactor-wi-init Refactor WI init to init function for more consistent startup

dafc4e8098a1732562844f28fb5ffec460434fc5

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

Signed
3 files changed, +104 -103Showing whitespace changes
public/script.js+2 -0
@@ -50,6 +50,7 @@ import {
50 importWorldInfo,50 importWorldInfo,
51 wi_anchor_position,51 wi_anchor_position,
52 world_info_include_names,52 world_info_include_names,
53 initWorldInfo,
53} from './scripts/world-info.js';54} from './scripts/world-info.js';
5455
55import {56import {
@@ -992,6 +993,7 @@ async function firstLoadInit() {
992 initBackgrounds();993 initBackgrounds();
993 initAuthorsNote();994 initAuthorsNote();
994 await initPersonas();995 await initPersonas();
996 initWorldInfo();
995 initRossMods();997 initRossMods();
996 initStats();998 initStats();
997 initCfg();999 initCfg();
public/scripts/utils.js+1 -1
@@ -1047,7 +1047,7 @@ export function getImageSizeFromDataURL(dataUrl) {
10471047
1048/**1048/**
1049 * Gets the filename of the character avatar without extension1049 * Gets the filename of the character avatar without extension
1050 * @param {number?} [chid=null] - Character ID. If not provided, uses the current character ID1050 * @param {string|number?} [chid=null] - Character ID. If not provided, uses the current character ID
1051 * @param {object} [options={}] - Options arguments1051 * @param {object} [options={}] - Options arguments
1052 * @param {string?} [options.manualAvatarKey=null] - Manually take the following avatar key, instead of using the chid to determine the name1052 * @param {string?} [options.manualAvatarKey=null] - Manually take the following avatar key, instead of using the chid to determine the name
1053 * @returns {string?} The filename of the character avatar without extension, or null if the character ID is invalid1053 * @returns {string?} The filename of the character avatar without extension, or null if the character ID is invalid
public/scripts/world-info.js+101 -102
@@ -966,7 +966,7 @@ function registerWorldInfoSlashCommands() {
966 /**966 /**
967 * Gets the name of the character-bound lorebook.967 * Gets the name of the character-bound lorebook.
968 * @param {import('./slash-commands/SlashCommand.js').NamedArguments} args Named arguments968 * @param {import('./slash-commands/SlashCommand.js').NamedArguments} args Named arguments
969 * @param {import('./slash-commands/SlashCommand.js').UnnamedArguments} name Character name969 * @param {string} name Character name
970 * @returns {string} The name of the character-bound lorebook, a JSON string of the character's lorebooks, or an empty string970 * @returns {string} The name of the character-bound lorebook, a JSON string of the character's lorebooks, or an empty string
971 */971 */
972 function getCharBookCallback({ type }, name) {972 function getCharBookCallback({ type }, name) {
@@ -3443,7 +3443,7 @@ function createEntryInputAutocomplete(input, callback, { allowMultiple = false }
3443 });3443 });
34443444
3445 $(input).on('focus click', function () {3445 $(input).on('focus click', function () {
3446 $(input).autocomplete('search', allowMultiple ? String($(input).val()).split(/,\s*/).pop() : $(input).val());3446 $(input).autocomplete('search', allowMultiple ? String($(input).val()).split(/,\s*/).pop() : String($(input).val()));
3447 });3447 });
3448}3448}
34493449
@@ -5118,7 +5118,7 @@ export function openWorldInfoEditor(worldName) {
51185118
5119/**5119/**
5120 * Assigns a lorebook to the current chat.5120 * Assigns a lorebook to the current chat.
5121 * @param {PointerEvent} event Pointer event5121 * @param {JQuery.ClickEvent<Document, undefined, any, any>} event Pointer event
5122 * @returns {Promise<void>}5122 * @returns {Promise<void>}
5123 */5123 */
5124export async function assignLorebookToChat(event) {5124export async function assignLorebookToChat(event) {
@@ -5157,11 +5157,106 @@ export async function assignLorebookToChat(event) {
5157 saveMetadata();5157 saveMetadata();
5158 });5158 });
51595159
5160 return callGenericPopup(template, POPUP_TYPE.TEXT);5160 await callGenericPopup(template, POPUP_TYPE.TEXT);
5161}5161}
51625162
5163jQuery(() => {5163/**
5164 * Moves a World Info entry from a source lorebook to a target lorebook.
5165 *
5166 * @param {string} sourceName - The name of the source lorebook file.
5167 * @param {string} targetName - The name of the target lorebook file.
5168 * @param {string|number} uid - The UID of the entry to move from the source lorebook.
5169 * @returns {Promise<boolean>} True if the move was successful, false otherwise.
5170 */
5171export async function moveWorldInfoEntry(sourceName, targetName, uid) {
5172 if (sourceName === targetName) {
5173 return false;
5174 }
5175
5176 if (!world_names.includes(sourceName)) {
5177 toastr.error(t`Source lorebook '${sourceName}' not found.`);
5178 console.error(`[WI Move] Source lorebook '${sourceName}' does not exist.`);
5179 return false;
5180 }
5181
5182 if (!world_names.includes(targetName)) {
5183 toastr.error(t`Target lorebook '${targetName}' not found.`);
5184 console.error(`[WI Move] Target lorebook '${targetName}' does not exist.`);
5185 return false;
5186 }
5187
5188 const entryUidString = String(uid);
5189
5190 try {
5191 const sourceData = await loadWorldInfo(sourceName);
5192 const targetData = await loadWorldInfo(targetName);
5193
5194 if (!sourceData || !sourceData.entries) {
5195 toastr.error(t`Failed to load data for source lorebook '${sourceName}'.`);
5196 console.error(`[WI Move] Could not load source data for '${sourceName}'.`);
5197 return false;
5198 }
5199 if (!targetData || !targetData.entries) {
5200 toastr.error(t`Failed to load data for target lorebook '${targetName}'.`);
5201 console.error(`[WI Move] Could not load target data for '${targetName}'.`);
5202 return false;
5203 }
5204
5205 if (!sourceData.entries[entryUidString]) {
5206 toastr.error(t`Entry not found in source lorebook '${sourceName}'.`);
5207 console.error(`[WI Move] Entry UID ${entryUidString} not found in '${sourceName}'.`);
5208 return false;
5209 }
5210
5211 const entryToMove = structuredClone(sourceData.entries[entryUidString]);
5212
5213
5214 const newUid = getFreeWorldEntryUid(targetData);
5215 if (newUid === null) {
5216 console.error(`[WI Move] Failed to get a free UID in '${targetName}'.`);
5217 return false;
5218 }
5219
5220 entryToMove.uid = newUid;
5221 // Place the entry at the end of the target lorebook
5222 const maxDisplayIndex = Object.values(targetData.entries).reduce((max, entry) => Math.max(max, entry.displayIndex ?? -1), -1);
5223 entryToMove.displayIndex = maxDisplayIndex + 1;
5224
5225 targetData.entries[newUid] = entryToMove;
5226
5227 delete sourceData.entries[entryUidString];
5228 // Remove from originalData if it exists
5229 deleteWIOriginalDataValue(sourceData, entryUidString);
5230 // TODO: setWIOriginalDataValue
5231 console.debug(`[WI Move] Removed entry UID ${entryUidString} from source '${sourceName}'.`);
5232
5233
5234 await saveWorldInfo(targetName, targetData, true);
5235 console.debug(`[WI Move] Saved target lorebook '${targetName}'.`);
5236 await saveWorldInfo(sourceName, sourceData, true);
5237 console.debug(`[WI Move] Saved source lorebook '${sourceName}'.`);
5238
5239
5240 console.log(`[WI Move] ${entryToMove.comment} moved successfully to '${targetName}'.`);
5241
5242 // Check if the currently viewed book in the editor is the source or target and reload it
5243 const currentEditorBookIndex = Number($('#world_editor_select').val());
5244 if (!isNaN(currentEditorBookIndex)) {
5245 const currentEditorBookName = world_names[currentEditorBookIndex];
5246 if (currentEditorBookName === sourceName || currentEditorBookName === targetName) {
5247 reloadEditor(currentEditorBookName);
5248 }
5249 }
5250
5251 return true;
5252 } catch (error) {
5253 toastr.error(t`An unexpected error occurred while moving the entry: ${error.message}`);
5254 console.error('[WI Move] Unexpected error:', error);
5255 return false;
5256 }
5257}
51645258
5259export function initWorldInfo() {
5165 $('#world_info').on('mousedown change', async function (e) {5260 $('#world_info').on('mousedown change', async function (e) {
5166 // If there's no world names, don't do anything5261 // If there's no world names, don't do anything
5167 if (world_names.length === 0) {5262 if (world_names.length === 0) {
@@ -5348,7 +5443,7 @@ jQuery(() => {
5348 if (!isMobile()) {5443 if (!isMobile()) {
5349 $('#world_info').select2({5444 $('#world_info').select2({
5350 width: '100%',5445 width: '100%',
5351 placeholder: 'No Worlds active. Click here to select.',5446 placeholder: t`No Worlds active. Click here to select.`,
5352 allowClear: true,5447 allowClear: true,
5353 closeOnSelect: false,5448 closeOnSelect: false,
5354 });5449 });
@@ -5373,100 +5468,4 @@ jQuery(() => {
5373 }5468 }
5374 });5469 });
5375 });5470 });
5376});
5377
5378/**
5379 * Moves a World Info entry from a source lorebook to a target lorebook.
5380 *
5381 * @param {string} sourceName - The name of the source lorebook file.
5382 * @param {string} targetName - The name of the target lorebook file.
5383 * @param {string|number} uid - The UID of the entry to move from the source lorebook.
5384 * @returns {Promise<boolean>} True if the move was successful, false otherwise.
5385 */
5386export async function moveWorldInfoEntry(sourceName, targetName, uid) {
5387 if (sourceName === targetName) {
5388 return false;
5389 }
5390
5391 if (!world_names.includes(sourceName)) {
5392 toastr.error(t`Source lorebook '${sourceName}' not found.`);
5393 console.error(`[WI Move] Source lorebook '${sourceName}' does not exist.`);
5394 return false;
5395 }
5396
5397 if (!world_names.includes(targetName)) {
5398 toastr.error(t`Target lorebook '${targetName}' not found.`);
5399 console.error(`[WI Move] Target lorebook '${targetName}' does not exist.`);
5400 return false;
5401 }
5402
5403 const entryUidString = String(uid);
5404
5405 try {
5406 const sourceData = await loadWorldInfo(sourceName);
5407 const targetData = await loadWorldInfo(targetName);
5408
5409 if (!sourceData || !sourceData.entries) {
5410 toastr.error(t`Failed to load data for source lorebook '${sourceName}'.`);
5411 console.error(`[WI Move] Could not load source data for '${sourceName}'.`);
5412 return false;
5413 }
5414 if (!targetData || !targetData.entries) {
5415 toastr.error(t`Failed to load data for target lorebook '${targetName}'.`);
5416 console.error(`[WI Move] Could not load target data for '${targetName}'.`);
5417 return false;
5418 }
5419
5420 if (!sourceData.entries[entryUidString]) {
5421 toastr.error(t`Entry not found in source lorebook '${sourceName}'.`);
5422 console.error(`[WI Move] Entry UID ${entryUidString} not found in '${sourceName}'.`);
5423 return false;
5424 }
5425
5426 const entryToMove = structuredClone(sourceData.entries[entryUidString]);
5427
5428
5429 const newUid = getFreeWorldEntryUid(targetData);
5430 if (newUid === null) {
5431 console.error(`[WI Move] Failed to get a free UID in '${targetName}'.`);
5432 return false;
5433 }
5434
5435 entryToMove.uid = newUid;
5436 // Place the entry at the end of the target lorebook
5437 const maxDisplayIndex = Object.values(targetData.entries).reduce((max, entry) => Math.max(max, entry.displayIndex ?? -1), -1);
5438 entryToMove.displayIndex = maxDisplayIndex + 1;
5439
5440 targetData.entries[newUid] = entryToMove;
5441
5442 delete sourceData.entries[entryUidString];
5443 // Remove from originalData if it exists
5444 deleteWIOriginalDataValue(sourceData, entryUidString);
5445 // TODO: setWIOriginalDataValue
5446 console.debug(`[WI Move] Removed entry UID ${entryUidString} from source '${sourceName}'.`);
5447
5448
5449 await saveWorldInfo(targetName, targetData, true);
5450 console.debug(`[WI Move] Saved target lorebook '${targetName}'.`);
5451 await saveWorldInfo(sourceName, sourceData, true);
5452 console.debug(`[WI Move] Saved source lorebook '${sourceName}'.`);
5453
5454
5455 console.log(`[WI Move] ${entryToMove.comment} moved successfully to '${targetName}'.`);
5456
5457 // Check if the currently viewed book in the editor is the source or target and reload it
5458 const currentEditorBookIndex = Number($('#world_editor_select').val());
5459 if (!isNaN(currentEditorBookIndex)) {
5460 const currentEditorBookName = world_names[currentEditorBookIndex];
5461 if (currentEditorBookName === sourceName || currentEditorBookName === targetName) {
5462 reloadEditor(currentEditorBookName);
5463 }
5464 }
5465
5466 return true;
5467 } catch (error) {
5468 toastr.error(t`An unexpected error occurred while moving the entry: ${error.message}`);
5469 console.error('[WI Move] Unexpected error:', error);
5470 return false;
5471 }
5472}5471}