Move function above init Well, I like that init is last in nearly all files...
| @@ -5160,6 +5160,102 @@ export async function assignLorebookToChat(event) { | |||
| 5160 | return callGenericPopup(template, POPUP_TYPE.TEXT); | 5160 | return callGenericPopup(template, POPUP_TYPE.TEXT); |
| 5161 | } | 5161 | } |
| 5162 | 5162 | ||
| 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 | */ | ||
| 5171 | export 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 | } | ||
| 5258 | |||
| 5163 | export function initWorldInfo() { | 5259 | export function initWorldInfo() { |
| 5164 | $('#world_info').on('mousedown change', async function (e) { | 5260 | $('#world_info').on('mousedown change', async function (e) { |
| 5165 | // If there's no world names, don't do anything | 5261 | // If there's no world names, don't do anything |
| @@ -5373,99 +5469,3 @@ export function initWorldInfo() { | |||
| 5373 | }); | 5469 | }); |
| 5374 | }); | 5470 | }); |
| 5375 | } | 5471 | } |
| 5376 | |||
| 5377 | /** | ||
| 5378 | * Moves a World Info entry from a source lorebook to a target lorebook. | ||
| 5379 | * | ||
| 5380 | * @param {string} sourceName - The name of the source lorebook file. | ||
| 5381 | * @param {string} targetName - The name of the target lorebook file. | ||
| 5382 | * @param {string|number} uid - The UID of the entry to move from the source lorebook. | ||
| 5383 | * @returns {Promise<boolean>} True if the move was successful, false otherwise. | ||
| 5384 | */ | ||
| 5385 | export async function moveWorldInfoEntry(sourceName, targetName, uid) { | ||
| 5386 | if (sourceName === targetName) { | ||
| 5387 | return false; | ||
| 5388 | } | ||
| 5389 | |||
| 5390 | if (!world_names.includes(sourceName)) { | ||
| 5391 | toastr.error(t`Source lorebook '${sourceName}' not found.`); | ||
| 5392 | console.error(`[WI Move] Source lorebook '${sourceName}' does not exist.`); | ||
| 5393 | return false; | ||
| 5394 | } | ||
| 5395 | |||
| 5396 | if (!world_names.includes(targetName)) { | ||
| 5397 | toastr.error(t`Target lorebook '${targetName}' not found.`); | ||
| 5398 | console.error(`[WI Move] Target lorebook '${targetName}' does not exist.`); | ||
| 5399 | return false; | ||
| 5400 | } | ||
| 5401 | |||
| 5402 | const entryUidString = String(uid); | ||
| 5403 | |||
| 5404 | try { | ||
| 5405 | const sourceData = await loadWorldInfo(sourceName); | ||
| 5406 | const targetData = await loadWorldInfo(targetName); | ||
| 5407 | |||
| 5408 | if (!sourceData || !sourceData.entries) { | ||
| 5409 | toastr.error(t`Failed to load data for source lorebook '${sourceName}'.`); | ||
| 5410 | console.error(`[WI Move] Could not load source data for '${sourceName}'.`); | ||
| 5411 | return false; | ||
| 5412 | } | ||
| 5413 | if (!targetData || !targetData.entries) { | ||
| 5414 | toastr.error(t`Failed to load data for target lorebook '${targetName}'.`); | ||
| 5415 | console.error(`[WI Move] Could not load target data for '${targetName}'.`); | ||
| 5416 | return false; | ||
| 5417 | } | ||
| 5418 | |||
| 5419 | if (!sourceData.entries[entryUidString]) { | ||
| 5420 | toastr.error(t`Entry not found in source lorebook '${sourceName}'.`); | ||
| 5421 | console.error(`[WI Move] Entry UID ${entryUidString} not found in '${sourceName}'.`); | ||
| 5422 | return false; | ||
| 5423 | } | ||
| 5424 | |||
| 5425 | const entryToMove = structuredClone(sourceData.entries[entryUidString]); | ||
| 5426 | |||
| 5427 | |||
| 5428 | const newUid = getFreeWorldEntryUid(targetData); | ||
| 5429 | if (newUid === null) { | ||
| 5430 | console.error(`[WI Move] Failed to get a free UID in '${targetName}'.`); | ||
| 5431 | return false; | ||
| 5432 | } | ||
| 5433 | |||
| 5434 | entryToMove.uid = newUid; | ||
| 5435 | // Place the entry at the end of the target lorebook | ||
| 5436 | const maxDisplayIndex = Object.values(targetData.entries).reduce((max, entry) => Math.max(max, entry.displayIndex ?? -1), -1); | ||
| 5437 | entryToMove.displayIndex = maxDisplayIndex + 1; | ||
| 5438 | |||
| 5439 | targetData.entries[newUid] = entryToMove; | ||
| 5440 | |||
| 5441 | delete sourceData.entries[entryUidString]; | ||
| 5442 | // Remove from originalData if it exists | ||
| 5443 | deleteWIOriginalDataValue(sourceData, entryUidString); | ||
| 5444 | // TODO: setWIOriginalDataValue | ||
| 5445 | console.debug(`[WI Move] Removed entry UID ${entryUidString} from source '${sourceName}'.`); | ||
| 5446 | |||
| 5447 | |||
| 5448 | await saveWorldInfo(targetName, targetData, true); | ||
| 5449 | console.debug(`[WI Move] Saved target lorebook '${targetName}'.`); | ||
| 5450 | await saveWorldInfo(sourceName, sourceData, true); | ||
| 5451 | console.debug(`[WI Move] Saved source lorebook '${sourceName}'.`); | ||
| 5452 | |||
| 5453 | |||
| 5454 | console.log(`[WI Move] ${entryToMove.comment} moved successfully to '${targetName}'.`); | ||
| 5455 | |||
| 5456 | // Check if the currently viewed book in the editor is the source or target and reload it | ||
| 5457 | const currentEditorBookIndex = Number($('#world_editor_select').val()); | ||
| 5458 | if (!isNaN(currentEditorBookIndex)) { | ||
| 5459 | const currentEditorBookName = world_names[currentEditorBookIndex]; | ||
| 5460 | if (currentEditorBookName === sourceName || currentEditorBookName === targetName) { | ||
| 5461 | reloadEditor(currentEditorBookName); | ||
| 5462 | } | ||
| 5463 | } | ||
| 5464 | |||
| 5465 | return true; | ||
| 5466 | } catch (error) { | ||
| 5467 | toastr.error(t`An unexpected error occurred while moving the entry: ${error.message}`); | ||
| 5468 | console.error('[WI Move] Unexpected error:', error); | ||
| 5469 | return false; | ||
| 5470 | } | ||
| 5471 | } | ||