| 5267 | }); | 5331 | }); |
| 5268 | }); | 5332 | }); |
| 5269 | }); | 5333 | }); |
| | 5334 | |
| | 5335 | /** |
| | 5336 | * Moves a World Info entry from a source lorebook to a target lorebook. |
| | 5337 | * |
| | 5338 | * @param {string} sourceName - The name of the source lorebook file. |
| | 5339 | * @param {string} targetName - The name of the target lorebook file. |
| | 5340 | * @param {number|string} uid - The UID of the entry to move from the source lorebook. |
| | 5341 | * @returns {Promise<boolean>} True if the move was successful, false otherwise. |
| | 5342 | */ |
| | 5343 | export async function moveWorldInfoEntry(sourceName, targetName, uid) { |
| | 5344 | console.log(`[WI] Attempting to move entry UID ${uid} from '${sourceName}' to '${targetName}'`); |
| | 5345 | |
| | 5346 | if (!sourceName || !targetName || uid === undefined || uid === null) { |
| | 5347 | console.error('[WI Move] Missing required arguments.'); |
| | 5348 | return false; |
| | 5349 | } |
| | 5350 | |
| | 5351 | if (sourceName === targetName) { |
| | 5352 | toastr.warning(t`Source and target lorebooks cannot be the same.`); |
| | 5353 | return false; |
| | 5354 | } |
| | 5355 | |
| | 5356 | if (!world_names.includes(sourceName)) { |
| | 5357 | toastr.error(t`Source lorebook '${sourceName}' not found.`); |
| | 5358 | console.error(`[WI Move] Source lorebook '${sourceName}' does not exist.`); |
| | 5359 | return false; |
| | 5360 | } |
| | 5361 | |
| | 5362 | if (!world_names.includes(targetName)) { |
| | 5363 | toastr.error(t`Target lorebook '${targetName}' not found.`); |
| | 5364 | console.error(`[WI Move] Target lorebook '${targetName}' does not exist.`); |
| | 5365 | return false; |
| | 5366 | } |
| | 5367 | |
| | 5368 | const entryUidString = String(uid); |
| | 5369 | |
| | 5370 | try { |
| | 5371 | const sourceData = await loadWorldInfo(sourceName); |
| | 5372 | const targetData = await loadWorldInfo(targetName); |
| | 5373 | |
| | 5374 | if (!sourceData || !sourceData.entries) { |
| | 5375 | toastr.error(t`Failed to load data for source lorebook '${sourceName}'.`); |
| | 5376 | console.error(`[WI Move] Could not load source data for '${sourceName}'.`); |
| | 5377 | return false; |
| | 5378 | } |
| | 5379 | if (!targetData || !targetData.entries) { |
| | 5380 | toastr.error(t`Failed to load data for target lorebook '${targetName}'.`); |
| | 5381 | console.error(`[WI Move] Could not load target data for '${targetName}'.`); |
| | 5382 | return false; |
| | 5383 | } |
| | 5384 | |
| | 5385 | if (!sourceData.entries[entryUidString]) { |
| | 5386 | toastr.error(t`Entry not found in source lorebook '${sourceName}'.`); |
| | 5387 | console.error(`[WI Move] Entry UID ${entryUidString} not found in '${sourceName}'.`); |
| | 5388 | return false; |
| | 5389 | } |
| | 5390 | |
| | 5391 | const entryToMove = structuredClone(sourceData.entries[entryUidString]); |
| | 5392 | |
| | 5393 | |
| | 5394 | const newUid = getFreeWorldEntryUid(targetData); |
| | 5395 | if (newUid === null) { |
| | 5396 | console.error(`[WI Move] Failed to get a free UID in '${targetName}'.`); |
| | 5397 | return false; |
| | 5398 | } |
| | 5399 | |
| | 5400 | entryToMove.uid = newUid; |
| | 5401 | // Reset displayIndex or let it be recalculated based on target book's sorting? |
| | 5402 | // For simplicity, let's assign a high index initially, assuming it might be sorted later. |
| | 5403 | // Or maybe better, find the max displayIndex in target and add 1? |
| | 5404 | const maxDisplayIndex = Object.values(targetData.entries).reduce((max, entry) => Math.max(max, entry.displayIndex ?? -1), -1); |
| | 5405 | entryToMove.displayIndex = maxDisplayIndex + 1; |
| | 5406 | |
| | 5407 | targetData.entries[newUid] = entryToMove; |
| | 5408 | |
| | 5409 | delete sourceData.entries[entryUidString]; |
| | 5410 | // Remove from originalData if it exists, using the original UID |
| | 5411 | deleteWIOriginalDataValue(sourceData, entryUidString); |
| | 5412 | console.debug(`[WI Move] Removed entry UID ${entryUidString} from source '${sourceName}'.`); |
| | 5413 | |
| | 5414 | |
| | 5415 | // Save immediately to reduce chances of inconsistency if the browser is closed |
| | 5416 | // Note: This is not truly atomic. If one save fails, state could be inconsistent. |
| | 5417 | await saveWorldInfo(targetName, targetData, true); |
| | 5418 | console.debug(`[WI Move] Saved target lorebook '${targetName}'.`); |
| | 5419 | await saveWorldInfo(sourceName, sourceData, true); |
| | 5420 | console.debug(`[WI Move] Saved source lorebook '${sourceName}'.`); |
| | 5421 | |
| | 5422 | |
| | 5423 | toastr.success(t`${entryToMove.comment} moved successfully!`); |
| | 5424 | |
| | 5425 | // Check if the currently viewed book in the editor is the source or target and reload it |
| | 5426 | const currentEditorBookIndex = Number($('#world_editor_select').val()); |
| | 5427 | if (!isNaN(currentEditorBookIndex)) { |
| | 5428 | const currentEditorBookName = world_names[currentEditorBookIndex]; |
| | 5429 | if (currentEditorBookName === sourceName || currentEditorBookName === targetName) { |
| | 5430 | reloadEditor(currentEditorBookName); |
| | 5431 | } |
| | 5432 | } |
| | 5433 | |
| | 5434 | return true; |
| | 5435 | } catch (error) { |
| | 5436 | toastr.error(t`An unexpected error occurred while moving the entry: ${error.message}`); |
| | 5437 | console.error('[WI Move] Unexpected error:', error); |
| | 5438 | return false; |
| | 5439 | } |
| | 5440 | } |