Merge pull request #3915 from SillyTavern/feat/refactor-wi-init

Refactor WI init to init function for more consistent startup
This commit is contained in:
Cohee
2025-04-25 22:28:55 +03:00
committed by GitHub
3 changed files with 104 additions and 103 deletions

View File

@@ -50,6 +50,7 @@ import {
importWorldInfo, importWorldInfo,
wi_anchor_position, wi_anchor_position,
world_info_include_names, world_info_include_names,
initWorldInfo,
} from './scripts/world-info.js'; } from './scripts/world-info.js';
import { import {
@@ -992,6 +993,7 @@ async function firstLoadInit() {
initBackgrounds(); initBackgrounds();
initAuthorsNote(); initAuthorsNote();
await initPersonas(); await initPersonas();
initWorldInfo();
initRossMods(); initRossMods();
initStats(); initStats();
initCfg(); initCfg();

View File

@@ -1047,7 +1047,7 @@ export function getImageSizeFromDataURL(dataUrl) {
/** /**
* Gets the filename of the character avatar without extension * Gets the filename of the character avatar without extension
* @param {number?} [chid=null] - Character ID. If not provided, uses the current character ID * @param {string|number?} [chid=null] - Character ID. If not provided, uses the current character ID
* @param {object} [options={}] - Options arguments * @param {object} [options={}] - Options arguments
* @param {string?} [options.manualAvatarKey=null] - Manually take the following avatar key, instead of using the chid to determine the name * @param {string?} [options.manualAvatarKey=null] - Manually take the following avatar key, instead of using the chid to determine the name
* @returns {string?} The filename of the character avatar without extension, or null if the character ID is invalid * @returns {string?} The filename of the character avatar without extension, or null if the character ID is invalid

View File

@@ -966,7 +966,7 @@ function registerWorldInfoSlashCommands() {
/** /**
* Gets the name of the character-bound lorebook. * Gets the name of the character-bound lorebook.
* @param {import('./slash-commands/SlashCommand.js').NamedArguments} args Named arguments * @param {import('./slash-commands/SlashCommand.js').NamedArguments} args Named arguments
* @param {import('./slash-commands/SlashCommand.js').UnnamedArguments} name Character name * @param {string} name Character name
* @returns {string} The name of the character-bound lorebook, a JSON string of the character's lorebooks, or an empty string * @returns {string} The name of the character-bound lorebook, a JSON string of the character's lorebooks, or an empty string
*/ */
function getCharBookCallback({ type }, name) { function getCharBookCallback({ type }, name) {
@@ -3443,7 +3443,7 @@ function createEntryInputAutocomplete(input, callback, { allowMultiple = false }
}); });
$(input).on('focus click', function () { $(input).on('focus click', function () {
$(input).autocomplete('search', allowMultiple ? String($(input).val()).split(/,\s*/).pop() : $(input).val()); $(input).autocomplete('search', allowMultiple ? String($(input).val()).split(/,\s*/).pop() : String($(input).val()));
}); });
} }
@@ -5118,7 +5118,7 @@ export function openWorldInfoEditor(worldName) {
/** /**
* Assigns a lorebook to the current chat. * Assigns a lorebook to the current chat.
* @param {PointerEvent} event Pointer event * @param {JQuery.ClickEvent<Document, undefined, any, any>} event Pointer event
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
export async function assignLorebookToChat(event) { export async function assignLorebookToChat(event) {
@@ -5157,11 +5157,106 @@ export async function assignLorebookToChat(event) {
saveMetadata(); saveMetadata();
}); });
return callGenericPopup(template, POPUP_TYPE.TEXT); await callGenericPopup(template, POPUP_TYPE.TEXT);
} }
jQuery(() => { /**
* Moves a World Info entry from a source lorebook to a target lorebook.
*
* @param {string} sourceName - The name of the source lorebook file.
* @param {string} targetName - The name of the target lorebook file.
* @param {string|number} uid - The UID of the entry to move from the source lorebook.
* @returns {Promise<boolean>} True if the move was successful, false otherwise.
*/
export async function moveWorldInfoEntry(sourceName, targetName, uid) {
if (sourceName === targetName) {
return false;
}
if (!world_names.includes(sourceName)) {
toastr.error(t`Source lorebook '${sourceName}' not found.`);
console.error(`[WI Move] Source lorebook '${sourceName}' does not exist.`);
return false;
}
if (!world_names.includes(targetName)) {
toastr.error(t`Target lorebook '${targetName}' not found.`);
console.error(`[WI Move] Target lorebook '${targetName}' does not exist.`);
return false;
}
const entryUidString = String(uid);
try {
const sourceData = await loadWorldInfo(sourceName);
const targetData = await loadWorldInfo(targetName);
if (!sourceData || !sourceData.entries) {
toastr.error(t`Failed to load data for source lorebook '${sourceName}'.`);
console.error(`[WI Move] Could not load source data for '${sourceName}'.`);
return false;
}
if (!targetData || !targetData.entries) {
toastr.error(t`Failed to load data for target lorebook '${targetName}'.`);
console.error(`[WI Move] Could not load target data for '${targetName}'.`);
return false;
}
if (!sourceData.entries[entryUidString]) {
toastr.error(t`Entry not found in source lorebook '${sourceName}'.`);
console.error(`[WI Move] Entry UID ${entryUidString} not found in '${sourceName}'.`);
return false;
}
const entryToMove = structuredClone(sourceData.entries[entryUidString]);
const newUid = getFreeWorldEntryUid(targetData);
if (newUid === null) {
console.error(`[WI Move] Failed to get a free UID in '${targetName}'.`);
return false;
}
entryToMove.uid = newUid;
// Place the entry at the end of the target lorebook
const maxDisplayIndex = Object.values(targetData.entries).reduce((max, entry) => Math.max(max, entry.displayIndex ?? -1), -1);
entryToMove.displayIndex = maxDisplayIndex + 1;
targetData.entries[newUid] = entryToMove;
delete sourceData.entries[entryUidString];
// Remove from originalData if it exists
deleteWIOriginalDataValue(sourceData, entryUidString);
// TODO: setWIOriginalDataValue
console.debug(`[WI Move] Removed entry UID ${entryUidString} from source '${sourceName}'.`);
await saveWorldInfo(targetName, targetData, true);
console.debug(`[WI Move] Saved target lorebook '${targetName}'.`);
await saveWorldInfo(sourceName, sourceData, true);
console.debug(`[WI Move] Saved source lorebook '${sourceName}'.`);
console.log(`[WI Move] ${entryToMove.comment} moved successfully to '${targetName}'.`);
// Check if the currently viewed book in the editor is the source or target and reload it
const currentEditorBookIndex = Number($('#world_editor_select').val());
if (!isNaN(currentEditorBookIndex)) {
const currentEditorBookName = world_names[currentEditorBookIndex];
if (currentEditorBookName === sourceName || currentEditorBookName === targetName) {
reloadEditor(currentEditorBookName);
}
}
return true;
} catch (error) {
toastr.error(t`An unexpected error occurred while moving the entry: ${error.message}`);
console.error('[WI Move] Unexpected error:', error);
return false;
}
}
export function initWorldInfo() {
$('#world_info').on('mousedown change', async function (e) { $('#world_info').on('mousedown change', async function (e) {
// If there's no world names, don't do anything // If there's no world names, don't do anything
if (world_names.length === 0) { if (world_names.length === 0) {
@@ -5348,7 +5443,7 @@ jQuery(() => {
if (!isMobile()) { if (!isMobile()) {
$('#world_info').select2({ $('#world_info').select2({
width: '100%', width: '100%',
placeholder: 'No Worlds active. Click here to select.', placeholder: t`No Worlds active. Click here to select.`,
allowClear: true, allowClear: true,
closeOnSelect: false, closeOnSelect: false,
}); });
@@ -5373,100 +5468,4 @@ jQuery(() => {
} }
}); });
}); });
});
/**
* Moves a World Info entry from a source lorebook to a target lorebook.
*
* @param {string} sourceName - The name of the source lorebook file.
* @param {string} targetName - The name of the target lorebook file.
* @param {string|number} uid - The UID of the entry to move from the source lorebook.
* @returns {Promise<boolean>} True if the move was successful, false otherwise.
*/
export async function moveWorldInfoEntry(sourceName, targetName, uid) {
if (sourceName === targetName) {
return false;
}
if (!world_names.includes(sourceName)) {
toastr.error(t`Source lorebook '${sourceName}' not found.`);
console.error(`[WI Move] Source lorebook '${sourceName}' does not exist.`);
return false;
}
if (!world_names.includes(targetName)) {
toastr.error(t`Target lorebook '${targetName}' not found.`);
console.error(`[WI Move] Target lorebook '${targetName}' does not exist.`);
return false;
}
const entryUidString = String(uid);
try {
const sourceData = await loadWorldInfo(sourceName);
const targetData = await loadWorldInfo(targetName);
if (!sourceData || !sourceData.entries) {
toastr.error(t`Failed to load data for source lorebook '${sourceName}'.`);
console.error(`[WI Move] Could not load source data for '${sourceName}'.`);
return false;
}
if (!targetData || !targetData.entries) {
toastr.error(t`Failed to load data for target lorebook '${targetName}'.`);
console.error(`[WI Move] Could not load target data for '${targetName}'.`);
return false;
}
if (!sourceData.entries[entryUidString]) {
toastr.error(t`Entry not found in source lorebook '${sourceName}'.`);
console.error(`[WI Move] Entry UID ${entryUidString} not found in '${sourceName}'.`);
return false;
}
const entryToMove = structuredClone(sourceData.entries[entryUidString]);
const newUid = getFreeWorldEntryUid(targetData);
if (newUid === null) {
console.error(`[WI Move] Failed to get a free UID in '${targetName}'.`);
return false;
}
entryToMove.uid = newUid;
// Place the entry at the end of the target lorebook
const maxDisplayIndex = Object.values(targetData.entries).reduce((max, entry) => Math.max(max, entry.displayIndex ?? -1), -1);
entryToMove.displayIndex = maxDisplayIndex + 1;
targetData.entries[newUid] = entryToMove;
delete sourceData.entries[entryUidString];
// Remove from originalData if it exists
deleteWIOriginalDataValue(sourceData, entryUidString);
// TODO: setWIOriginalDataValue
console.debug(`[WI Move] Removed entry UID ${entryUidString} from source '${sourceName}'.`);
await saveWorldInfo(targetName, targetData, true);
console.debug(`[WI Move] Saved target lorebook '${targetName}'.`);
await saveWorldInfo(sourceName, sourceData, true);
console.debug(`[WI Move] Saved source lorebook '${sourceName}'.`);
console.log(`[WI Move] ${entryToMove.comment} moved successfully to '${targetName}'.`);
// Check if the currently viewed book in the editor is the source or target and reload it
const currentEditorBookIndex = Number($('#world_editor_select').val());
if (!isNaN(currentEditorBookIndex)) {
const currentEditorBookName = world_names[currentEditorBookIndex];
if (currentEditorBookName === sourceName || currentEditorBookName === targetName) {
reloadEditor(currentEditorBookName);
}
}
return true;
} catch (error) {
toastr.error(t`An unexpected error occurred while moving the entry: ${error.message}`);
console.error('[WI Move] Unexpected error:', error);
return false;
}
} }