/** * User Stats Rendering Module * Handles rendering of the user stats panel with progress bars and classic RPG stats */ import { getContext } from '../../../../../../extensions.js'; import { user_avatar } from '../../../../../../../script.js'; import { extensionSettings, lastGeneratedData, committedTrackerData, $userStatsContainer, FALLBACK_AVATAR_DATA_URI } from '../../core/state.js'; import { saveSettings, saveChatData, updateMessageSwipeData } from '../../core/persistence.js'; import { getSafeThumbnailUrl } from '../../utils/avatars.js'; import { buildInventorySummary } from '../generation/promptBuilder.js'; /** * Renders the user stats panel with health bars, mood, inventory, and classic stats. * Includes event listeners for editable fields. */ export function renderUserStats() { if (!extensionSettings.showUserStats || !$userStatsContainer) { return; } const stats = extensionSettings.userStats; const userName = getContext().name1; // Initialize lastGeneratedData.userStats if it doesn't exist if (!lastGeneratedData.userStats) { lastGeneratedData.userStats = `Health: ${stats.health}%\nSatiety: ${stats.satiety}%\nEnergy: ${stats.energy}%\nHygiene: ${stats.hygiene}%\nArousal: ${stats.arousal}%\n${stats.mood}: ${stats.conditions}\nInventory: ${stats.inventory}`; } // Get user portrait - handle both default-user and custom persona folders // Use a base64-encoded SVG placeholder as fallback to avoid 400 errors let userPortrait = FALLBACK_AVATAR_DATA_URI; if (user_avatar) { // Try to get the thumbnail using our safe helper const thumbnailUrl = getSafeThumbnailUrl('persona', user_avatar); if (thumbnailUrl) { userPortrait = thumbnailUrl; } } // Create gradient from low to high color const gradient = `linear-gradient(to right, ${extensionSettings.statBarColorLow}, ${extensionSettings.statBarColorHigh})`; const html = `
${userName}
Health:
${stats.health}%
Satiety:
${stats.satiety}%
Energy:
${stats.energy}%
Hygiene:
${stats.hygiene}%
Arousal:
${stats.arousal}%
${stats.mood}
${stats.conditions}
STR
${extensionSettings.classicStats.str}
DEX
${extensionSettings.classicStats.dex}
CON
${extensionSettings.classicStats.con}
INT
${extensionSettings.classicStats.int}
WIS
${extensionSettings.classicStats.wis}
CHA
${extensionSettings.classicStats.cha}
`; $userStatsContainer.html(html); // Add event listeners for editable stat values $('.rpg-editable-stat').on('blur', function() { const field = $(this).data('field'); const textValue = $(this).text().replace('%', '').trim(); let value = parseInt(textValue); // Validate and clamp value between 0 and 100 if (isNaN(value)) { value = 0; } value = Math.max(0, Math.min(100, value)); // Update the setting extensionSettings.userStats[field] = value; // Rebuild userStats text with proper inventory format const stats = extensionSettings.userStats; const inventorySummary = buildInventorySummary(stats.inventory); const statsText = `Health: ${stats.health}%\nSatiety: ${stats.satiety}%\nEnergy: ${stats.energy}%\nHygiene: ${stats.hygiene}%\nArousal: ${stats.arousal}%\n${stats.mood}: ${stats.conditions}\n${inventorySummary}`; // Update BOTH lastGeneratedData AND committedTrackerData // This makes manual edits immediately visible to AI lastGeneratedData.userStats = statsText; committedTrackerData.userStats = statsText; saveSettings(); saveChatData(); updateMessageSwipeData(); // Re-render to update the bar renderUserStats(); }); // Add event listeners for mood/conditions editing $('.rpg-mood-emoji.rpg-editable').on('blur', function() { const value = $(this).text().trim(); extensionSettings.userStats.mood = value || '😐'; // Rebuild userStats text with proper inventory format const stats = extensionSettings.userStats; const inventorySummary = buildInventorySummary(stats.inventory); const statsText = `Health: ${stats.health}%\nSatiety: ${stats.satiety}%\nEnergy: ${stats.energy}%\nHygiene: ${stats.hygiene}%\nArousal: ${stats.arousal}%\n${stats.mood}: ${stats.conditions}\n${inventorySummary}`; // Update BOTH lastGeneratedData AND committedTrackerData // This makes manual edits immediately visible to AI lastGeneratedData.userStats = statsText; committedTrackerData.userStats = statsText; saveSettings(); saveChatData(); updateMessageSwipeData(); }); $('.rpg-mood-conditions.rpg-editable').on('blur', function() { const value = $(this).text().trim(); extensionSettings.userStats.conditions = value || 'None'; // Rebuild userStats text with proper inventory format const stats = extensionSettings.userStats; const inventorySummary = buildInventorySummary(stats.inventory); const statsText = `Health: ${stats.health}%\nSatiety: ${stats.satiety}%\nEnergy: ${stats.energy}%\nHygiene: ${stats.hygiene}%\nArousal: ${stats.arousal}%\n${stats.mood}: ${stats.conditions}\n${inventorySummary}`; // Update BOTH lastGeneratedData AND committedTrackerData // This makes manual edits immediately visible to AI lastGeneratedData.userStats = statsText; committedTrackerData.userStats = statsText; saveSettings(); saveChatData(); updateMessageSwipeData(); }); }