diff --git a/index.js b/index.js index 68864a1..17c9e62 100644 --- a/index.js +++ b/index.js @@ -93,6 +93,46 @@ let $userStatsContainer = null; let $infoBoxContainer = null; let $thoughtsContainer = null; +/** + * Safely attempts to get a thumbnail URL with proper error handling. + * Returns null if the URL cannot be generated to avoid 400 Bad Request errors. + * + * @param {string} type - The type of thumbnail ('persona' or 'avatar') + * @param {string} filename - The filename to get thumbnail for + * @returns {string|null} - The thumbnail URL or null if it fails + */ +function getSafeThumbnailUrl(type, filename) { + // Return null if no filename provided + if (!filename || filename === 'none') { + console.log(`[RPG Companion] No valid filename provided for ${type} thumbnail`); + return null; + } + + try { + // Attempt to get thumbnail URL from SillyTavern API + const url = getThumbnailUrl(type, filename); + + // Validate that we got a string back + if (typeof url !== 'string' || url.trim() === '') { + console.warn(`[RPG Companion] getThumbnailUrl returned invalid result for ${type}:`, filename); + return null; + } + + console.log(`[RPG Companion] Successfully generated ${type} thumbnail URL for: ${filename}`); + return url; + } catch (error) { + // Log detailed error information for debugging + console.error(`[RPG Companion] Failed to get ${type} thumbnail for "${filename}":`, error); + console.error('[RPG Companion] Error details:', { + type, + filename, + errorMessage: error.message, + errorStack: error.stack + }); + return null; + } +} + /** * Loads the extension settings from the global settings object. */ @@ -2739,14 +2779,15 @@ function renderUserStats() { } // Get user portrait - handle both default-user and custom persona folders - let userPortrait = 'img/user-default.png'; // fallback + // Use a transparent placeholder as fallback to avoid 400 errors + const transparentPixel = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="100" height="100"%3E%3Crect width="100" height="100" fill="%23cccccc" opacity="0.3"/%3E%3Ctext x="50%25" y="50%25" text-anchor="middle" dy=".3em" fill="%23666" font-size="40"%3E%3F%3C/text%3E%3C/svg%3E'; + let userPortrait = transparentPixel; + if (user_avatar) { - // Try to get the thumbnail, but have a fallback - try { - userPortrait = getThumbnailUrl('persona', user_avatar) || 'img/user-default.png'; - } catch (e) { - console.warn('[RPG Companion] Could not load user avatar, using default', e); - userPortrait = 'img/user-default.png'; + // Try to get the thumbnail using our safe helper + const thumbnailUrl = getSafeThumbnailUrl('persona', user_avatar); + if (thumbnailUrl) { + userPortrait = thumbnailUrl; } } @@ -2757,7 +2798,7 @@ function renderUserStats() {
- ${userName} + ${userName}
${stats.inventory || 'None'} @@ -3251,12 +3292,17 @@ function renderThoughts() { // If no characters parsed, show a placeholder editable card if (presentCharacters.length === 0) { // Get default character portrait (try to use the current character if in 1-on-1 chat) - let defaultPortrait = 'img/user-default.png'; + // Use a transparent placeholder as fallback to avoid 400 errors + const transparentPixel = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="100" height="100"%3E%3Crect width="100" height="100" fill="%23cccccc" opacity="0.3"/%3E%3Ctext x="50%25" y="50%25" text-anchor="middle" dy=".3em" fill="%23666" font-size="40"%3E%3F%3C/text%3E%3C/svg%3E'; + let defaultPortrait = transparentPixel; let defaultName = 'Character'; if (this_chid !== undefined && characters[this_chid]) { if (characters[this_chid].avatar && characters[this_chid].avatar !== 'none') { - defaultPortrait = getThumbnailUrl('avatar', characters[this_chid].avatar); + const thumbnailUrl = getSafeThumbnailUrl('avatar', characters[this_chid].avatar); + if (thumbnailUrl) { + defaultPortrait = thumbnailUrl; + } } defaultName = characters[this_chid].name || 'Character'; } @@ -3265,7 +3311,7 @@ function renderThoughts() { html += `
- ${defaultName} + ${defaultName}
⚖️
@@ -3282,7 +3328,9 @@ function renderThoughts() { html += '
'; for (const char of presentCharacters) { // Find character portrait - let characterPortrait = 'img/user-default.png'; + // Use a transparent placeholder as fallback to avoid 400 errors + const transparentPixel = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="100" height="100"%3E%3Crect width="100" height="100" fill="%23cccccc" opacity="0.3"/%3E%3Ctext x="50%25" y="50%25" text-anchor="middle" dy=".3em" fill="%23666" font-size="40"%3E%3F%3C/text%3E%3C/svg%3E'; + let characterPortrait = transparentPixel; // console.log('[RPG Companion] Looking for avatar for:', char.name); @@ -3294,25 +3342,34 @@ function renderThoughts() { ); if (matchingMember && matchingMember.avatar && matchingMember.avatar !== 'none') { - characterPortrait = getThumbnailUrl('avatar', matchingMember.avatar); + const thumbnailUrl = getSafeThumbnailUrl('avatar', matchingMember.avatar); + if (thumbnailUrl) { + characterPortrait = thumbnailUrl; + } } } // For regular chats or if not found in group, search all characters - if (characterPortrait === 'img/user-default.png' && characters && characters.length > 0) { + if (characterPortrait === transparentPixel && characters && characters.length > 0) { const matchingCharacter = characters.find(c => c && c.name && c.name.toLowerCase() === char.name.toLowerCase() ); if (matchingCharacter && matchingCharacter.avatar && matchingCharacter.avatar !== 'none') { - characterPortrait = getThumbnailUrl('avatar', matchingCharacter.avatar); + const thumbnailUrl = getSafeThumbnailUrl('avatar', matchingCharacter.avatar); + if (thumbnailUrl) { + characterPortrait = thumbnailUrl; + } } } // If this is the current character in a 1-on-1 chat, use their portrait if (this_chid !== undefined && characters[this_chid] && characters[this_chid].name && characters[this_chid].name.toLowerCase() === char.name.toLowerCase()) { - characterPortrait = getThumbnailUrl('avatar', characters[this_chid].avatar); + const thumbnailUrl = getSafeThumbnailUrl('avatar', characters[this_chid].avatar); + if (thumbnailUrl) { + characterPortrait = thumbnailUrl; + } } // Get relationship emoji @@ -3321,7 +3378,7 @@ function renderThoughts() { html += `
- ${char.name} + ${char.name}
${relationshipEmoji}
@@ -4594,23 +4651,33 @@ async function ensureHtmlCleaningRegex() { */ function updatePersonaAvatar() { const portraitImg = document.querySelector('.rpg-user-portrait'); - if (!portraitImg) return; + if (!portraitImg) { + console.log('[RPG Companion] Portrait image element not found in DOM'); + return; + } // Get current user_avatar from context instead of using imported value const context = getContext(); const currentUserAvatar = context.user_avatar || user_avatar; - let userPortrait = 'img/user-default.png'; - if (currentUserAvatar) { - try { - userPortrait = getThumbnailUrl('persona', currentUserAvatar) || 'img/user-default.png'; - } catch (e) { - console.warn('[RPG Companion] Could not load user avatar, using default', e); - userPortrait = 'img/user-default.png'; - } - } + console.log('[RPG Companion] Attempting to update persona avatar:', currentUserAvatar); - portraitImg.src = userPortrait; + // Try to get a valid thumbnail URL using our safe helper + if (currentUserAvatar) { + const thumbnailUrl = getSafeThumbnailUrl('persona', currentUserAvatar); + + if (thumbnailUrl) { + // Only update the src if we got a valid URL + portraitImg.src = thumbnailUrl; + console.log('[RPG Companion] Persona avatar updated successfully'); + } else { + // Don't update the src if we couldn't get a valid URL + // This prevents 400 errors and keeps the existing image + console.warn('[RPG Companion] Could not get valid thumbnail URL for persona avatar, keeping existing image'); + } + } else { + console.log('[RPG Companion] No user avatar configured, keeping existing image'); + } } /**