diff --git a/src/systems/generation/parser.js b/src/systems/generation/parser.js index e11e2b9..2472ffc 100644 --- a/src/systems/generation/parser.js +++ b/src/systems/generation/parser.js @@ -46,6 +46,31 @@ function separateEmojiFromText(str) { return { emoji: '', text: str }; } +/** + * Helper to strip enclosing brackets from text + * Removes [], {}, and () from the entire text if it's wrapped + * @param {string} text - Text that may be wrapped in brackets + * @returns {string} Text with brackets removed + */ +function stripBrackets(text) { + if (!text) return text; + + // Remove leading and trailing whitespace first + text = text.trim(); + + // Check if the entire text is wrapped in brackets and remove them + // This handles cases where models wrap entire sections in brackets + while ( + (text.startsWith('[') && text.endsWith(']')) || + (text.startsWith('{') && text.endsWith('}')) || + (text.startsWith('(') && text.endsWith(')')) + ) { + text = text.substring(1, text.length - 1).trim(); + } + + return text; +} + /** * Helper to log to both console and debug logs array */ @@ -76,9 +101,15 @@ export function parseResponse(responseText) { debugLog('[RPG Parser] Response length:', responseText.length + ' chars'); debugLog('[RPG Parser] First 500 chars:', responseText.substring(0, 500)); + // Remove content inside thinking tags first (model's internal reasoning) + // This prevents parsing code blocks from the model's thinking process + let cleanedResponse = responseText.replace(/[\s\S]*?<\/think>/gi, ''); + cleanedResponse = cleanedResponse.replace(/[\s\S]*?<\/thinking>/gi, ''); + debugLog('[RPG Parser] Removed thinking tags, new length:', cleanedResponse.length + ' chars'); + // Extract code blocks const codeBlockRegex = /```([^`]+)```/g; - const matches = [...responseText.matchAll(codeBlockRegex)]; + const matches = [...cleanedResponse.matchAll(codeBlockRegex)]; debugLog('[RPG Parser] Found', matches.length + ' code blocks'); @@ -102,21 +133,21 @@ export function parseResponse(responseText) { // Extract User Stats section const statsMatch = content.match(/(User )?Stats\s*\n\s*---[\s\S]*?(?=\n\s*\n\s*(Info Box|Present Characters)|$)/i); if (statsMatch && !result.userStats) { - result.userStats = statsMatch[0].trim(); + result.userStats = stripBrackets(statsMatch[0].trim()); debugLog('[RPG Parser] ✓ Extracted Stats from combined block'); } // Extract Info Box section const infoBoxMatch = content.match(/Info Box\s*\n\s*---[\s\S]*?(?=\n\s*\n\s*Present Characters|$)/i); if (infoBoxMatch && !result.infoBox) { - result.infoBox = infoBoxMatch[0].trim(); + result.infoBox = stripBrackets(infoBoxMatch[0].trim()); debugLog('[RPG Parser] ✓ Extracted Info Box from combined block'); } // Extract Present Characters section const charactersMatch = content.match(/Present Characters\s*\n\s*---[\s\S]*$/i); if (charactersMatch && !result.characterThoughts) { - result.characterThoughts = charactersMatch[0].trim(); + result.characterThoughts = stripBrackets(charactersMatch[0].trim()); debugLog('[RPG Parser] ✓ Extracted Present Characters from combined block'); } } else { @@ -146,13 +177,13 @@ export function parseResponse(responseText) { (content.includes(" | ") && (content.includes("Thoughts") || content.includes("💭"))); if (isStats && !result.userStats) { - result.userStats = content; + result.userStats = stripBrackets(content); debugLog('[RPG Parser] ✓ Matched: Stats section'); } else if (isInfoBox && !result.infoBox) { - result.infoBox = content; + result.infoBox = stripBrackets(content); debugLog('[RPG Parser] ✓ Matched: Info Box section'); } else if (isCharacters && !result.characterThoughts) { - result.characterThoughts = content; + result.characterThoughts = stripBrackets(content); debugLog('[RPG Parser] ✓ Matched: Present Characters section'); debugLog('[RPG Parser] Full content:', content); } else { diff --git a/src/systems/rendering/infoBox.js b/src/systems/rendering/infoBox.js index 33bf98e..c42d4c9 100644 --- a/src/systems/rendering/infoBox.js +++ b/src/systems/rendering/infoBox.js @@ -64,8 +64,11 @@ export function renderInfoBox() { $infoBoxContainer.addClass('rpg-content-updating'); } + // Use committedTrackerData as fallback if lastGeneratedData is empty (e.g., after page refresh) + const infoBoxData = lastGeneratedData.infoBox || committedTrackerData.infoBox; + // If no data yet, show placeholder - if (!lastGeneratedData.infoBox) { + if (!infoBoxData) { const placeholderHtml = `
@@ -81,10 +84,10 @@ export function renderInfoBox() { return; } - // console.log('[RPG Companion] renderInfoBox called with data:', lastGeneratedData.infoBox); + // console.log('[RPG Companion] renderInfoBox called with data:', infoBoxData); // Parse the info box data - const lines = lastGeneratedData.infoBox.split('\n'); + const lines = infoBoxData.split('\n'); // console.log('[RPG Companion] Info Box split into lines:', lines); const data = { date: '', diff --git a/src/systems/rendering/thoughts.js b/src/systems/rendering/thoughts.js index 02f073c..a4c0d89 100644 --- a/src/systems/rendering/thoughts.js +++ b/src/systems/rendering/thoughts.js @@ -76,15 +76,13 @@ export function renderThoughts() { $thoughtsContainer.addClass('rpg-content-updating'); } - // Initialize if no data yet - if (!lastGeneratedData.characterThoughts) { - lastGeneratedData.characterThoughts = ''; - } + // Use committedTrackerData as fallback if lastGeneratedData is empty (e.g., after page refresh) + const characterThoughtsData = lastGeneratedData.characterThoughts || committedTrackerData.characterThoughts || ''; - debugLog('[RPG Thoughts] Raw characterThoughts data:', lastGeneratedData.characterThoughts); - debugLog('[RPG Thoughts] Data length:', lastGeneratedData.characterThoughts.length + ' chars'); + debugLog('[RPG Thoughts] Raw characterThoughts data:', characterThoughtsData); + debugLog('[RPG Thoughts] Data length:', characterThoughtsData.length + ' chars'); - const lines = lastGeneratedData.characterThoughts.split('\n'); + const lines = characterThoughtsData.split('\n'); const presentCharacters = []; debugLog('[RPG Thoughts] Split into lines count:', lines.length); @@ -378,8 +376,14 @@ export function updateCharacterField(characterName, field, value) { if (emojiMatch) { let emoji = emojiMatch[1].trim(); let info = emojiMatch[2].trim(); - let relationship = parts[1]; - let thoughts = parts[2] || ''; + let relationship = parts[1] ? parts[1].trim() : ''; + let thoughts = parts[2] ? parts[2].trim() : ''; + + // Handle 4-part format (with demeanor) + if (parts.length >= 4) { + relationship = parts[2] ? parts[2].trim() : ''; + thoughts = parts[3] ? parts[3].trim() : ''; + } const infoParts = info.split(',').map(p => p.trim()); let name = infoParts[0];