diff --git a/src/systems/generation/promptBuilder.js b/src/systems/generation/promptBuilder.js index 62be66c..456acde 100644 --- a/src/systems/generation/promptBuilder.js +++ b/src/systems/generation/promptBuilder.js @@ -256,7 +256,7 @@ export function generateTrackerInstructions(includeHtmlPrompt = true, includeCon // Relationship line (only if relationships are enabled) if (relationshipPlaceholders) { - instructions += `Relationship: [${relationshipPlaceholders}]\n`; + instructions += `Relationship: [(choose one: ${relationshipPlaceholders})]\n`; } // Stats line (if enabled) diff --git a/src/systems/rendering/thoughts.js b/src/systems/rendering/thoughts.js index df8029b..5f8a3cd 100644 --- a/src/systems/rendering/thoughts.js +++ b/src/systems/rendering/thoughts.js @@ -27,6 +27,14 @@ function debugLog(message, data = null) { } } +/** + * Escapes HTML attribute values to prevent quotes from breaking HTML + */ +function escapeHtmlAttr(str) { + if (!str) return ''; + return String(str).replace(/"/g, '"').replace(/'/g, '''); +} + /** * Interpolates color based on percentage value between low and high colors * @param {number} percentage - Value from 0-100 @@ -78,10 +86,12 @@ function namesMatch(cardName, aiName) { // 1. Exact match (fast path) if (cardName.toLowerCase() === aiName.toLowerCase()) return true; - // 2. Strip parentheses and match - const stripParens = (s) => s.replace(/\s*\([^)]*\)/g, '').trim(); - const cardCore = stripParens(cardName).toLowerCase(); - const aiCore = stripParens(aiName).toLowerCase(); + // 2. Strip parentheses and quotes from both names and match + // This allows "Dottore (Prime)" to match "Dottore" card for avatar lookup + // and "Marianna "Mari"" to match "Marianna" or "Mari" cards + const stripParensAndQuotes = (s) => s.replace(/\s*\([^)]*\)/g, '').replace(/["']/g, '').trim(); + const cardCore = stripParensAndQuotes(cardName).toLowerCase(); + const aiCore = stripParensAndQuotes(aiName).toLowerCase(); if (cardCore === aiCore) return true; // 3. Check if card name appears as complete word in AI name @@ -249,17 +259,19 @@ export function renderThoughts() { defaultName = characters[this_chid].name || 'Character'; } + const escapedDefaultName = escapeHtmlAttr(defaultName); + html += '
'; html += ` -
+
- ${defaultName} -
⚖️
+ ${escapedDefaultName} +
⚖️
- 😊 - ${defaultName} + 😊 + ${defaultName}
`; @@ -267,7 +279,7 @@ export function renderThoughts() { for (const field of enabledFields) { const fieldId = field.name.toLowerCase().replace(/\s+/g, '-'); html += ` -
+
`; } @@ -361,17 +373,20 @@ export function renderThoughts() { debugLog(`[RPG Thoughts] Building HTML card for ${char.name}...`); + // Escape character name for use in HTML attributes + const escapedName = escapeHtmlAttr(char.name); + html += ` -
+
- ${char.name} - ${hasRelationshipEnabled ? `
${relationshipBadge}
` : ''} + ${escapedName} + ${hasRelationshipEnabled ? `
${relationshipBadge}
` : ''}
- ${char.emoji} - ${char.name} + ${char.emoji} + ${char.name}
`; @@ -380,7 +395,7 @@ export function renderThoughts() { const fieldValue = char[field.name] || ''; const fieldId = field.name.toLowerCase().replace(/\s+/g, '-'); html += ` -
${fieldValue}
+
${fieldValue}
`; } @@ -396,7 +411,7 @@ export function renderThoughts() { const statColor = getStatColor(statValue, extensionSettings.statBarColorLow, extensionSettings.statBarColorHigh); html += `
- ${stat.name}: ${statValue}% + ${stat.name}: ${statValue}%
`; } @@ -817,12 +832,13 @@ export function createThoughtPanel($message, thoughtsArray) { // Build thought bubbles HTML let thoughtsHtml = ''; thoughtsArray.forEach((thought, index) => { + const escapedThoughtName = escapeHtmlAttr(thought.name); thoughtsHtml += `
${thought.emoji}
-
+
${thought.thought}