/** * Inventory Item Editing Module * Handles inline editing of inventory item names */ import { extensionSettings, lastGeneratedData, committedTrackerData } from '../../core/state.js'; import { saveSettings, saveChatData, updateMessageSwipeData } from '../../core/persistence.js'; import { buildInventorySummary } from '../generation/promptBuilder.js'; import { renderInventory } from '../rendering/inventory.js'; import { parseItems, serializeItems } from '../../utils/itemParser.js'; import { sanitizeItemName } from '../../utils/security.js'; /** * Updates an existing inventory item's name. * Validates, sanitizes, and persists the change. * * @param {string} field - Field name ('onPerson', 'stored', 'assets') * @param {number} index - Index of item in the array * @param {string} newName - New name for the item * @param {string} [location] - Location name (required for 'stored' field) */ export function updateInventoryItem(field, index, newName, location) { const inventory = extensionSettings.userStats.inventory; // Validate and sanitize the new item name const sanitizedName = sanitizeItemName(newName); if (!sanitizedName) { console.warn('[RPG Companion] Invalid item name, reverting change'); // Re-render to revert the change in UI renderInventory(); return; } // Get current items for the field let currentString; if (field === 'stored') { if (!location) { console.error('[RPG Companion] Location required for stored items'); return; } currentString = inventory.stored[location] || 'None'; } else { currentString = inventory[field] || 'None'; } // Parse current items const items = parseItems(currentString); // Validate index if (index < 0 || index >= items.length) { console.error(`[RPG Companion] Invalid item index: ${index}`); return; } // Update the item at this index items[index] = sanitizedName; // Serialize back to string const newItemString = serializeItems(items); // Update the inventory if (field === 'stored') { inventory.stored[location] = newItemString; } else { inventory[field] = newItemString; } // Update lastGeneratedData and committedTrackerData with new inventory updateLastGeneratedDataInventory(); // Save changes saveSettings(); saveChatData(); updateMessageSwipeData(); // Re-render inventory renderInventory(); } /** * Updates lastGeneratedData.userStats AND committedTrackerData.userStats to include * current inventory. * Maintains JSON format if current data is JSON, otherwise uses text format. * This ensures manual edits are immediately visible to AI in next generation. * @private */ function updateLastGeneratedDataInventory() { // Check if current data is in JSON format const currentData = lastGeneratedData.userStats || committedTrackerData.userStats; if (currentData) { const trimmed = currentData.trim(); if (trimmed.startsWith('{') || trimmed.startsWith('[')) { // Maintain JSON format try { const jsonData = JSON.parse(currentData); if (jsonData && typeof jsonData === 'object') { // Update inventory in JSON const stats = extensionSettings.userStats; // Convert inventory back to v3 format (arrays of {name, quantity}) const convertToV3Items = (itemString) => { if (!itemString) return []; const items = itemString.split(',').map(s => s.trim()).filter(s => s); return items.map(item => { const qtyMatch = item.match(/^(\d+)x\s+(.+)$/); if (qtyMatch) { return { name: qtyMatch[2].trim(), quantity: parseInt(qtyMatch[1]) }; } return { name: item, quantity: 1 }; }); }; jsonData.inventory = { onPerson: convertToV3Items(stats.inventory.onPerson), clothing: convertToV3Items(stats.inventory.clothing), stored: stats.inventory.stored || {}, assets: convertToV3Items(stats.inventory.assets) }; const updatedJSON = JSON.stringify(jsonData, null, 2); lastGeneratedData.userStats = updatedJSON; committedTrackerData.userStats = updatedJSON; return; } } catch (e) { console.warn('[RPG Companion] Failed to parse JSON, falling back to text format:', e); } } } // Fall back to text format const stats = extensionSettings.userStats; const inventorySummary = buildInventorySummary(stats.inventory); const statsText = `Health: ${stats.health}%\n` + `Satiety: ${stats.satiety}%\n` + `Energy: ${stats.energy}%\n` + `Hygiene: ${stats.hygiene}%\n` + `Arousal: ${stats.arousal}%\n` + `${stats.mood}: ${stats.conditions}\n` + `${inventorySummary}`; lastGeneratedData.userStats = statsText; committedTrackerData.userStats = statsText; }