v3.1.0: Add parser error detection and recommended models section
This commit is contained in:
@@ -75,23 +75,23 @@ function separateEmojiFromText(str) {
|
||||
* Includes event listeners for editable fields.
|
||||
*/
|
||||
export function renderInfoBox() {
|
||||
console.log('[RPG InfoBox Render] ==================== RENDERING INFO BOX ====================');
|
||||
console.log('[RPG InfoBox Render] showInfoBox setting:', extensionSettings.showInfoBox);
|
||||
console.log('[RPG InfoBox Render] Container exists:', !!$infoBoxContainer);
|
||||
// console.log('[RPG InfoBox Render] ==================== RENDERING INFO BOX ====================');
|
||||
// console.log('[RPG InfoBox Render] showInfoBox setting:', extensionSettings.showInfoBox);
|
||||
// console.log('[RPG InfoBox Render] Container exists:', !!$infoBoxContainer);
|
||||
|
||||
if (!extensionSettings.showInfoBox || !$infoBoxContainer) {
|
||||
console.log('[RPG InfoBox Render] Exiting: showInfoBox or container is false');
|
||||
// console.log('[RPG InfoBox Render] Exiting: showInfoBox or container is false');
|
||||
return;
|
||||
}
|
||||
|
||||
// Use committedTrackerData as fallback if lastGeneratedData is empty (e.g., after page refresh)
|
||||
const infoBoxData = lastGeneratedData.infoBox || committedTrackerData.infoBox;
|
||||
console.log('[RPG InfoBox Render] infoBoxData length:', infoBoxData ? infoBoxData.length : 'null');
|
||||
console.log('[RPG InfoBox Render] infoBoxData preview:', infoBoxData ? infoBoxData.substring(0, 200) : 'null');
|
||||
// console.log('[RPG InfoBox Render] infoBoxData length:', infoBoxData ? infoBoxData.length : 'null');
|
||||
// console.log('[RPG InfoBox Render] infoBoxData preview:', infoBoxData ? infoBoxData.substring(0, 200) : 'null');
|
||||
|
||||
// If no data yet, hide the container (e.g., after cache clear)
|
||||
if (!infoBoxData) {
|
||||
console.log('[RPG InfoBox Render] No data, hiding container');
|
||||
// console.log('[RPG InfoBox Render] No data, hiding container');
|
||||
$infoBoxContainer.empty().hide();
|
||||
return;
|
||||
}
|
||||
@@ -574,6 +574,19 @@ export function renderInfoBox() {
|
||||
|
||||
$infoBoxContainer.html(html);
|
||||
|
||||
// Add dynamic text scaling for location field
|
||||
const updateLocationTextSize = ($element) => {
|
||||
const text = $element.text();
|
||||
const charCount = text.length;
|
||||
$element.css('--char-count', Math.min(charCount, 100));
|
||||
};
|
||||
|
||||
// Initial size update for location
|
||||
const $locationText = $infoBoxContainer.find('[data-field="location"]');
|
||||
if ($locationText.length) {
|
||||
updateLocationTextSize($locationText);
|
||||
}
|
||||
|
||||
// Add event handlers for editable Info Box fields
|
||||
$infoBoxContainer.find('.rpg-editable').on('blur', function() {
|
||||
const $this = $(this);
|
||||
@@ -591,6 +604,11 @@ export function renderInfoBox() {
|
||||
}
|
||||
}
|
||||
|
||||
// Update location text size dynamically
|
||||
if (field === 'location') {
|
||||
updateLocationTextSize($this);
|
||||
}
|
||||
|
||||
// Handle recent events separately
|
||||
if (field === 'event1' || field === 'event2' || field === 'event3') {
|
||||
updateRecentEvent(field, value);
|
||||
@@ -599,6 +617,11 @@ export function renderInfoBox() {
|
||||
}
|
||||
});
|
||||
|
||||
// Update location size on input as well (real-time)
|
||||
$infoBoxContainer.find('[data-field="location"]').on('input', function() {
|
||||
updateLocationTextSize($(this));
|
||||
});
|
||||
|
||||
// For date fields, show full value on focus
|
||||
$infoBoxContainer.find('[data-field="month"], [data-field="weekday"], [data-field="year"]').on('focus', function() {
|
||||
const fullValue = $(this).data('full-value');
|
||||
@@ -707,7 +730,7 @@ export function updateInfoBoxField(field, value) {
|
||||
committedTrackerData.infoBox = lastGeneratedData.infoBox;
|
||||
saveChatData();
|
||||
renderInfoBox();
|
||||
console.log('[RPG Companion] Updated info box field (v3 JSON):', { field, value });
|
||||
// console.log('[RPG Companion] Updated info box field (v3 JSON):', { field, value });
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1061,6 +1084,6 @@ function updateRecentEvent(field, value) {
|
||||
window.RPGCompanion.updateWeatherEffect();
|
||||
}
|
||||
|
||||
console.log(`[RPG Companion] Updated recent event ${field}:`, value);
|
||||
// console.log(`[RPG Companion] Updated recent event ${field}:`, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ export function renderMusicPlayer(container) {
|
||||
// Find the chat form container and insert widget before (above) it
|
||||
const $chatForm = $('#send_form');
|
||||
|
||||
console.log('[RPG Companion] Music Player: Found #send_form:', $chatForm.length > 0);
|
||||
// console.log('[RPG Companion] Music Player: Found #send_form:', $chatForm.length > 0);
|
||||
|
||||
if ($chatForm.length === 0) {
|
||||
console.error('[RPG Companion] Music Player: Could not find #send_form - cannot render widget!');
|
||||
@@ -108,17 +108,17 @@ export function renderMusicPlayer(container) {
|
||||
}
|
||||
|
||||
// Insert widget inside (at top of) the chat form
|
||||
console.log('[RPG Companion] Music Player: Prepending widget to #send_form');
|
||||
// console.log('[RPG Companion] Music Player: Prepending widget to #send_form');
|
||||
$chatForm.prepend(musicPlayerHtml);
|
||||
|
||||
console.log('[RPG Companion] Music Player: Widget inserted, checking if visible...');
|
||||
// console.log('[RPG Companion] Music Player: Widget inserted, checking if visible...');
|
||||
const $widget = $('#rpg-chat-music-player');
|
||||
console.log('[RPG Companion] Music Player: Widget exists:', $widget.length > 0);
|
||||
// console.log('[RPG Companion] Music Player: Widget exists:', $widget.length > 0);
|
||||
if ($widget.length > 0) {
|
||||
console.log('[RPG Companion] Music Player: Widget position:', $widget.offset());
|
||||
console.log('[RPG Companion] Music Player: Widget dimensions:', { width: $widget.width(), height: $widget.height() });
|
||||
console.log('[RPG Companion] Music Player: Widget CSS display:', $widget.css('display'));
|
||||
console.log('[RPG Companion] Music Player: Widget CSS visibility:', $widget.css('visibility'));
|
||||
// console.log('[RPG Companion] Music Player: Widget position:', $widget.offset());
|
||||
// console.log('[RPG Companion] Music Player: Widget dimensions:', { width: $widget.width(), height: $widget.height() });
|
||||
// console.log('[RPG Companion] Music Player: Widget CSS display:', $widget.css('display'));
|
||||
// console.log('[RPG Companion] Music Player: Widget CSS visibility:', $widget.css('visibility'));
|
||||
}
|
||||
|
||||
// Bind play button click
|
||||
|
||||
@@ -39,7 +39,7 @@ function getLockIconHtml(tracker, path) {
|
||||
* Helper to log to both console and debug logs array
|
||||
*/
|
||||
function debugLog(message, data = null) {
|
||||
console.log(message, data || '');
|
||||
// console.log(message, data || '');
|
||||
if (extensionSettings.debugMode) {
|
||||
addDebugLog(message, data);
|
||||
}
|
||||
@@ -592,7 +592,7 @@ export function renderThoughts() {
|
||||
const character = $(this).data('character');
|
||||
const field = $(this).data('field');
|
||||
const value = $(this).text().trim();
|
||||
console.log('[RPG Companion] Character stat edit:', { character, field, value });
|
||||
// console.log('[RPG Companion] Character stat edit:', { character, field, value });
|
||||
updateCharacterField(character, field, value);
|
||||
});
|
||||
|
||||
@@ -723,7 +723,7 @@ export function updateCharacterField(characterName, field, value) {
|
||||
else if (isThoughtsField && line.startsWith(thoughtsFieldName + ':')) {
|
||||
// Update thoughts field
|
||||
lines[i] = `${thoughtsFieldName}: ${value}`;
|
||||
console.log('[RPG Companion] Updated thoughts:', lines[i]);
|
||||
// console.log('[RPG Companion] Updated thoughts:', lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -737,7 +737,7 @@ export function updateCharacterField(characterName, field, value) {
|
||||
}
|
||||
numValue = Math.max(0, Math.min(100, numValue));
|
||||
|
||||
console.log('[RPG Companion] Updating stat:', { field, rawValue: value, cleanValue, numValue });
|
||||
// console.log('[RPG Companion] Updating stat:', { field, rawValue: value, cleanValue, numValue });
|
||||
|
||||
if (statsLineExists) {
|
||||
// Update existing Stats line
|
||||
@@ -750,7 +750,7 @@ export function updateCharacterField(characterName, field, value) {
|
||||
if (statParts[j].startsWith(field + ':')) {
|
||||
statParts[j] = `${field}: ${numValue}%`;
|
||||
statFound = true;
|
||||
console.log('[RPG Companion] Updated stat part:', statParts[j]);
|
||||
// console.log('[RPG Companion] Updated stat part:', statParts[j]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -758,11 +758,11 @@ export function updateCharacterField(characterName, field, value) {
|
||||
// If stat wasn't found in existing parts, add it
|
||||
if (!statFound) {
|
||||
statParts.push(`${field}: ${numValue}%`);
|
||||
console.log('[RPG Companion] Added new stat to existing line:', `${field}: ${numValue}%`);
|
||||
// console.log('[RPG Companion] Added new stat to existing line:', `${field}: ${numValue}%`);
|
||||
}
|
||||
|
||||
lines[statsLineIndex] = `Stats: ${statParts.join(' | ')}`;
|
||||
console.log('[RPG Companion] Updated stats line:', lines[statsLineIndex]);
|
||||
// console.log('[RPG Companion] Updated stats line:', lines[statsLineIndex]);
|
||||
} else {
|
||||
// Create new Stats line with all enabled stats (defaulting to 0% except the one being edited)
|
||||
const statsParts = enabledCharStats.map(s => {
|
||||
@@ -785,7 +785,7 @@ export function updateCharacterField(characterName, field, value) {
|
||||
}
|
||||
|
||||
lines.splice(insertIndex, 0, newStatsLine);
|
||||
console.log('[RPG Companion] Created new stats line:', newStatsLine);
|
||||
// console.log('[RPG Companion] Created new stats line:', newStatsLine);
|
||||
characterEndIndex++; // Adjust end index since we inserted a line
|
||||
}
|
||||
}
|
||||
@@ -831,7 +831,7 @@ export function updateCharacterField(characterName, field, value) {
|
||||
lastGeneratedData.characterThoughts = lines.join('\n');
|
||||
committedTrackerData.characterThoughts = lines.join('\n');
|
||||
|
||||
console.log('[RPG Companion] Updated characterThoughts data:', lastGeneratedData.characterThoughts);
|
||||
// console.log('[RPG Companion] Updated characterThoughts data:', lastGeneratedData.characterThoughts);
|
||||
|
||||
const chat = getContext().chat;
|
||||
if (chat && chat.length > 0) {
|
||||
@@ -1075,12 +1075,12 @@ function initThoughtIconDragHandlers() {
|
||||
if (thoughtIconDragHandlersInitialized) return;
|
||||
thoughtIconDragHandlersInitialized = true;
|
||||
|
||||
console.log('[Thought Icon] Initializing drag handlers ONCE - will attach to icon when created');
|
||||
// console.log('[Thought Icon] Initializing drag handlers ONCE - will attach to icon when created');
|
||||
}
|
||||
|
||||
// Function to attach drag handlers to a specific icon element
|
||||
function attachDragHandlersToIcon($icon) {
|
||||
console.log('[Thought Icon] Attaching handlers to icon element');
|
||||
// console.log('[Thought Icon] Attaching handlers to icon element');
|
||||
|
||||
// Remove any existing handlers
|
||||
$icon.off('.thoughtIconDrag');
|
||||
@@ -1089,20 +1089,20 @@ function attachDragHandlersToIcon($icon) {
|
||||
$icon.on('click.thoughtIconDrag', function(e) {
|
||||
// Check global flag set immediately after drag completes
|
||||
if (justFinishedDragging) {
|
||||
console.log('[Thought Icon] CLICK blocked - just finished dragging');
|
||||
// console.log('[Thought Icon] CLICK blocked - just finished dragging');
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
e.stopImmediatePropagation();
|
||||
return false;
|
||||
}
|
||||
console.log('[Thought Icon] CLICK detected on icon!');
|
||||
// console.log('[Thought Icon] CLICK detected on icon!');
|
||||
});
|
||||
|
||||
// Touch drag support - mobile only
|
||||
$icon.on('touchstart.thoughtIconDrag', function(e) {
|
||||
if (window.innerWidth > 1000) return;
|
||||
|
||||
console.log('[Thought Icon] touchstart');
|
||||
// console.log('[Thought Icon] touchstart');
|
||||
touchMoved = false;
|
||||
dragStartTime = Date.now();
|
||||
const touch = e.originalEvent.touches[0];
|
||||
@@ -1120,7 +1120,7 @@ function attachDragHandlersToIcon($icon) {
|
||||
if (window.innerWidth > 1000) return;
|
||||
|
||||
if (!touchMoved) {
|
||||
console.log('[Thought Icon] touchmove - first movement');
|
||||
// console.log('[Thought Icon] touchmove - first movement');
|
||||
}
|
||||
touchMoved = true;
|
||||
const touch = e.originalEvent.touches[0];
|
||||
@@ -1160,7 +1160,7 @@ function attachDragHandlersToIcon($icon) {
|
||||
});
|
||||
|
||||
$icon.on('touchend.thoughtIconDrag', function(e) {
|
||||
console.log('[Thought Icon] touchend - isDragging:', isDragging, 'touchMoved:', touchMoved);
|
||||
// console.log('[Thought Icon] touchend - isDragging:', isDragging, 'touchMoved:', touchMoved);
|
||||
|
||||
if (isDragging) {
|
||||
const offset = $(this).offset();
|
||||
@@ -1193,7 +1193,7 @@ function attachDragHandlersToIcon($icon) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
} else if (!touchMoved) {
|
||||
console.log('[Thought Icon] Opening panel - was a tap');
|
||||
// console.log('[Thought Icon] Opening panel - was a tap');
|
||||
const $panel = $('#rpg-thought-panel');
|
||||
const iconOffset = $(this).offset();
|
||||
if (iconOffset) {
|
||||
@@ -1207,7 +1207,7 @@ function attachDragHandlersToIcon($icon) {
|
||||
$(this).addClass('rpg-hidden');
|
||||
$panel.fadeIn(200);
|
||||
} else {
|
||||
console.log('[Thought Icon] Did nothing - touchMoved but not isDragging');
|
||||
// console.log('[Thought Icon] Did nothing - touchMoved but not isDragging');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1217,7 +1217,7 @@ function attachDragHandlersToIcon($icon) {
|
||||
$icon.on('mousedown.thoughtIconDrag', function(e) {
|
||||
if (window.innerWidth > 1000) return;
|
||||
|
||||
console.log('[Thought Icon] mousedown');
|
||||
// console.log('[Thought Icon] mousedown');
|
||||
e.preventDefault();
|
||||
|
||||
mouseDown = true;
|
||||
@@ -1237,7 +1237,7 @@ function attachDragHandlersToIcon($icon) {
|
||||
if (!mouseDown || window.innerWidth > 1000) return;
|
||||
|
||||
if (!touchMoved) {
|
||||
console.log('[Thought Icon] mousemove - first movement');
|
||||
// console.log('[Thought Icon] mousemove - first movement');
|
||||
}
|
||||
touchMoved = true;
|
||||
|
||||
@@ -1280,7 +1280,7 @@ function attachDragHandlersToIcon($icon) {
|
||||
$(document).on('mouseup.thoughtIconDrag', function(e) {
|
||||
if (!mouseDown) return;
|
||||
|
||||
console.log('[Thought Icon] mouseup - isDragging:', isDragging, 'touchMoved:', touchMoved);
|
||||
// console.log('[Thought Icon] mouseup - isDragging:', isDragging, 'touchMoved:', touchMoved);
|
||||
|
||||
mouseDown = false;
|
||||
|
||||
@@ -1568,22 +1568,82 @@ export function createThoughtPanel($message, thoughtsArray) {
|
||||
// Load saved icon position in mobile, or default to center of viewport
|
||||
if (extensionSettings.thoughtIconPosition && extensionSettings.thoughtIconPosition.top && extensionSettings.thoughtIconPosition.left) {
|
||||
const pos = extensionSettings.thoughtIconPosition;
|
||||
$thoughtIcon.css({
|
||||
top: pos.top,
|
||||
left: pos.left,
|
||||
transform: 'none',
|
||||
right: 'auto',
|
||||
bottom: 'auto'
|
||||
});
|
||||
|
||||
// Validate saved position - check if it's not at the very top (likely invalid)
|
||||
const savedTop = parseInt(pos.top);
|
||||
const topBar = $('#top-settings-holder');
|
||||
const topBarHeight = topBar.length ? topBar.outerHeight() : 60;
|
||||
|
||||
// If saved position is above or too close to top bar, recalculate default
|
||||
if (savedTop < topBarHeight + 50) {
|
||||
// console.log('[Thought Icon] Saved position invalid (too close to top), recalculating default');
|
||||
// Clear invalid saved position
|
||||
delete extensionSettings.thoughtIconPosition;
|
||||
saveSettings();
|
||||
|
||||
// Calculate new default position
|
||||
setTimeout(() => {
|
||||
const viewportHeight = window.innerHeight;
|
||||
const viewportWidth = window.innerWidth;
|
||||
|
||||
const defaultTop = topBarHeight + ((viewportHeight - topBarHeight) / 2) - 22;
|
||||
const defaultLeft = (viewportWidth * 0.75) - 22;
|
||||
|
||||
// console.log('[Thought Icon] Setting new default position:', {
|
||||
// topBarHeight,
|
||||
// viewportHeight,
|
||||
// viewportWidth,
|
||||
// calculatedTop: defaultTop,
|
||||
// calculatedLeft: defaultLeft
|
||||
// });
|
||||
|
||||
$thoughtIcon.css({
|
||||
top: `${defaultTop}px`,
|
||||
left: `${defaultLeft}px`,
|
||||
transform: 'none',
|
||||
right: 'auto',
|
||||
bottom: 'auto'
|
||||
});
|
||||
}, 100);
|
||||
} else {
|
||||
// Position is valid, use it
|
||||
$thoughtIcon.css({
|
||||
top: pos.top,
|
||||
left: pos.left,
|
||||
transform: 'none',
|
||||
right: 'auto',
|
||||
bottom: 'auto'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Default position: center of viewport
|
||||
$thoughtIcon.css({
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
right: 'auto',
|
||||
bottom: 'auto'
|
||||
});
|
||||
// Default position: center-right of viewport, accounting for top bar
|
||||
// Use setTimeout to ensure DOM is fully rendered before calculating positions
|
||||
setTimeout(() => {
|
||||
const topBar = $('#top-settings-holder');
|
||||
const topBarHeight = topBar.length ? topBar.outerHeight() : 60;
|
||||
const viewportHeight = window.innerHeight;
|
||||
const viewportWidth = window.innerWidth;
|
||||
|
||||
// Position in the center vertically (accounting for top bar) and slightly right
|
||||
const defaultTop = topBarHeight + ((viewportHeight - topBarHeight) / 2) - 22; // 22 = half of icon height
|
||||
const defaultLeft = (viewportWidth * 0.75) - 22; // 75% from left, minus half icon width
|
||||
|
||||
// console.log('[Thought Icon] Setting default position:', {
|
||||
// topBarHeight,
|
||||
// viewportHeight,
|
||||
// viewportWidth,
|
||||
// calculatedTop: defaultTop,
|
||||
// calculatedLeft: defaultLeft
|
||||
// });
|
||||
|
||||
$thoughtIcon.css({
|
||||
top: `${defaultTop}px`,
|
||||
left: `${defaultLeft}px`,
|
||||
transform: 'none',
|
||||
right: 'auto',
|
||||
bottom: 'auto'
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
} else {
|
||||
// Desktop: show panel, hide icon with class
|
||||
|
||||
@@ -181,12 +181,12 @@ export function renderUserStats() {
|
||||
|
||||
// Don't render if no data exists (e.g., after cache clear)
|
||||
// Check both lastGeneratedData and committedTrackerData
|
||||
console.log('[RPG UserStats Render] Checking data:', {
|
||||
hasLastGenerated: !!lastGeneratedData.userStats,
|
||||
hasCommitted: !!committedTrackerData.userStats,
|
||||
lastGeneratedPreview: lastGeneratedData.userStats ? lastGeneratedData.userStats.substring(0, 100) : 'null',
|
||||
committedPreview: committedTrackerData.userStats ? committedTrackerData.userStats.substring(0, 100) : 'null'
|
||||
});
|
||||
// console.log('[RPG UserStats Render] Checking data:', {
|
||||
// hasLastGenerated: !!lastGeneratedData.userStats,
|
||||
// hasCommitted: !!committedTrackerData.userStats,
|
||||
// lastGeneratedPreview: lastGeneratedData.userStats ? lastGeneratedData.userStats.substring(0, 100) : 'null',
|
||||
// committedPreview: committedTrackerData.userStats ? committedTrackerData.userStats.substring(0, 100) : 'null'
|
||||
// });
|
||||
|
||||
if (!lastGeneratedData.userStats && !committedTrackerData.userStats) {
|
||||
// Always render to the #rpg-user-stats container (mobile layout just moves it around in DOM)
|
||||
@@ -200,15 +200,15 @@ export function renderUserStats() {
|
||||
}
|
||||
|
||||
const stats = extensionSettings.userStats;
|
||||
console.log('[RPG UserStats Render] Current extensionSettings.userStats:', {
|
||||
health: stats.health,
|
||||
satiety: stats.satiety,
|
||||
energy: stats.energy,
|
||||
hygiene: stats.hygiene,
|
||||
arousal: stats.arousal,
|
||||
mood: stats.mood,
|
||||
conditions: stats.conditions
|
||||
});
|
||||
// console.log('[RPG UserStats Render] Current extensionSettings.userStats:', {
|
||||
// health: stats.health,
|
||||
// satiety: stats.satiety,
|
||||
// energy: stats.energy,
|
||||
// hygiene: stats.hygiene,
|
||||
// arousal: stats.arousal,
|
||||
// mood: stats.mood,
|
||||
// conditions: stats.conditions
|
||||
// });
|
||||
const config = extensionSettings.trackerConfig?.userStats || {
|
||||
customStats: [
|
||||
{ id: 'health', name: 'Health', enabled: true },
|
||||
@@ -394,13 +394,13 @@ export function renderUserStats() {
|
||||
|
||||
html += '</div>'; // Close rpg-stats-content
|
||||
|
||||
console.log('[RPG UserStats Render] Generated HTML length:', html.length);
|
||||
console.log('[RPG UserStats Render] HTML preview:', html.substring(0, 300));
|
||||
console.log('[RPG UserStats Render] Container exists:', !!$userStatsContainer, '$userStatsContainer length:', $userStatsContainer?.length);
|
||||
// console.log('[RPG UserStats Render] Generated HTML length:', html.length);
|
||||
// console.log('[RPG UserStats Render] HTML preview:', html.substring(0, 300));
|
||||
// console.log('[RPG UserStats Render] Container exists:', !!$userStatsContainer, '$userStatsContainer length:', $userStatsContainer?.length);
|
||||
|
||||
// Always render to the #rpg-user-stats container (mobile layout just moves it around in DOM)
|
||||
$userStatsContainer.html(html);
|
||||
console.log('[RPG UserStats Render] ✓ HTML rendered to #rpg-user-stats container');
|
||||
// console.log('[RPG UserStats Render] ✓ HTML rendered to #rpg-user-stats container');
|
||||
|
||||
// Add event listeners for editable stat values
|
||||
$('.rpg-editable-stat').on('blur', function() {
|
||||
|
||||
Reference in New Issue
Block a user