fix(widgets): resolve data reading issues for Recent Events and Present Characters
Issue 1: Recent Events widget not updating Root cause: Widget was reading from settings.committedTrackerData.infoBox (only updated on first gen) instead of using getInfoBoxData() from dependencies (updated every gen) Fix: - Changed registerRecentEventsWidget to use getInfoBoxData() pattern - Updated render() to destructure getInfoBoxData from dependencies - Updated attachRecentEventsHandlers to accept dependencies - Rewrote updateRecentEvent to use getInfoBoxData/setInfoBoxData/onDataChange - Now matches pattern used by other infoBox widgets (calendar, weather, etc.) Issue 2: Present Characters showing 'Details' + granny emoji Root cause: Parser expected OLD single-line format but AI returns NEW multi-line format from tracker customization system OLD format: 🧐: Name, Traits | Relationship | Thoughts NEW format: - Name Details: 🧐 | Traits Relationship: Type Thoughts: Text Fix: - Rewrote parseCharacterThoughts() to handle multi-line format: - Detects character entries starting with `-` - Parses Details: line for emoji and traits - Parses Relationship: line - Parses Thoughts: line (removes surrounding quotes) - Skips optional Stats: line - Kept legacy single-line format as fallback for backward compatibility Data flow verified: AI response → parseResponse() → extensionSettings.infoBoxData → getInfoBoxData() → widget render() Result: ✅ Recent Events displays: "Morning meditation, bathing, dressing" ✅ Present Characters displays: Full character cards with emoji, traits, relationship, thoughts ✅ Both widgets update on every AI generation ✅ Backward compatible with old format
This commit is contained in:
@@ -494,8 +494,6 @@ function attachSimpleEditHandlers(container, dependencies) {
|
|||||||
* @param {Function} dependencies.saveSettings - Save settings
|
* @param {Function} dependencies.saveSettings - Save settings
|
||||||
*/
|
*/
|
||||||
export function registerRecentEventsWidget(registry, dependencies) {
|
export function registerRecentEventsWidget(registry, dependencies) {
|
||||||
const { getExtensionSettings, saveSettings } = dependencies;
|
|
||||||
|
|
||||||
registry.register('recentEvents', {
|
registry.register('recentEvents', {
|
||||||
name: 'Recent Events',
|
name: 'Recent Events',
|
||||||
icon: '📝',
|
icon: '📝',
|
||||||
@@ -511,9 +509,8 @@ export function registerRecentEventsWidget(registry, dependencies) {
|
|||||||
* @param {Object} config - Widget configuration
|
* @param {Object} config - Widget configuration
|
||||||
*/
|
*/
|
||||||
render(container, config = {}) {
|
render(container, config = {}) {
|
||||||
const settings = getExtensionSettings();
|
const { getInfoBoxData } = dependencies;
|
||||||
const infoBoxData = settings.committedTrackerData?.infoBox || '';
|
const data = parseInfoBoxData(getInfoBoxData());
|
||||||
const data = parseInfoBoxData(infoBoxData);
|
|
||||||
|
|
||||||
// Merge default config with user config
|
// Merge default config with user config
|
||||||
const finalConfig = {
|
const finalConfig = {
|
||||||
@@ -574,7 +571,7 @@ export function registerRecentEventsWidget(registry, dependencies) {
|
|||||||
container.innerHTML = html;
|
container.innerHTML = html;
|
||||||
|
|
||||||
// Attach event handlers
|
// Attach event handlers
|
||||||
attachRecentEventsHandlers(container, settings, saveSettings);
|
attachRecentEventsHandlers(container, dependencies);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -609,7 +606,7 @@ export function registerRecentEventsWidget(registry, dependencies) {
|
|||||||
* Attach event handlers for Recent Events widget
|
* Attach event handlers for Recent Events widget
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function attachRecentEventsHandlers(container, settings, saveSettings) {
|
function attachRecentEventsHandlers(container, dependencies) {
|
||||||
const eventFields = container.querySelectorAll('.rpg-editable-event');
|
const eventFields = container.querySelectorAll('.rpg-editable-event');
|
||||||
|
|
||||||
eventFields.forEach(field => {
|
eventFields.forEach(field => {
|
||||||
@@ -641,7 +638,7 @@ function attachRecentEventsHandlers(container, settings, saveSettings) {
|
|||||||
|
|
||||||
// Update if changed
|
// Update if changed
|
||||||
if (value !== originalValue) {
|
if (value !== originalValue) {
|
||||||
updateRecentEvent(eventIndex, value, settings, saveSettings);
|
updateRecentEvent(eventIndex, value, dependencies);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -670,9 +667,11 @@ function attachRecentEventsHandlers(container, settings, saveSettings) {
|
|||||||
* Update a specific recent event in infoBox data
|
* Update a specific recent event in infoBox data
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
function updateRecentEvent(eventIndex, value, settings, saveSettings) {
|
function updateRecentEvent(eventIndex, value, dependencies) {
|
||||||
|
const { getInfoBoxData, setInfoBoxData, onDataChange } = dependencies;
|
||||||
|
|
||||||
// Parse current infoBox to get existing events
|
// Parse current infoBox to get existing events
|
||||||
const infoBoxData = settings.committedTrackerData?.infoBox || '';
|
const infoBoxData = getInfoBoxData() || '';
|
||||||
const lines = infoBoxData.split('\n');
|
const lines = infoBoxData.split('\n');
|
||||||
let recentEvents = [];
|
let recentEvents = [];
|
||||||
|
|
||||||
@@ -715,14 +714,13 @@ function updateRecentEvent(eventIndex, value, settings, saveSettings) {
|
|||||||
|
|
||||||
const updatedInfoBox = updatedLines.join('\n');
|
const updatedInfoBox = updatedLines.join('\n');
|
||||||
|
|
||||||
// Update committed and last generated data
|
// Save using dependency function (handles all necessary updates)
|
||||||
settings.committedTrackerData.infoBox = updatedInfoBox;
|
setInfoBoxData(updatedInfoBox);
|
||||||
if (settings.lastGeneratedData) {
|
|
||||||
settings.lastGeneratedData.infoBox = updatedInfoBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save settings
|
// Notify change
|
||||||
saveSettings();
|
if (onDataChange) {
|
||||||
|
onDataChange('infoBox', 'recentEvents', value, { eventIndex });
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`[Recent Events Widget] Updated event ${eventIndex}: "${value}"`);
|
console.log(`[Recent Events Widget] Updated event ${eventIndex}: "${value}"`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,56 +43,95 @@ function parseCharacterThoughts(thoughtsText) {
|
|||||||
|
|
||||||
const lines = thoughtsText.split('\n');
|
const lines = thoughtsText.split('\n');
|
||||||
const presentCharacters = [];
|
const presentCharacters = [];
|
||||||
|
let currentChar = null;
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
// Skip empty lines, headers, dividers
|
const trimmed = line.trim();
|
||||||
if (!line.trim() ||
|
|
||||||
line.includes('Present Characters') ||
|
// Skip headers, dividers, and empty lines
|
||||||
line.includes('---') ||
|
if (!trimmed ||
|
||||||
line.trim().startsWith('```')) {
|
trimmed.includes('Present Characters') ||
|
||||||
|
trimmed.includes('---') ||
|
||||||
|
trimmed.startsWith('```')) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parts = line.split('|').map(p => p.trim());
|
// New character entry (starts with -)
|
||||||
|
if (trimmed.startsWith('-')) {
|
||||||
|
// Save previous character
|
||||||
|
if (currentChar && currentChar.name && currentChar.name.toLowerCase() !== 'unavailable') {
|
||||||
|
presentCharacters.push(currentChar);
|
||||||
|
}
|
||||||
|
|
||||||
// Require at least 3 parts: Emoji:Name | Relationship | Thoughts
|
// Start new character
|
||||||
if (parts.length >= 3) {
|
const name = trimmed.replace(/^-\s*/, '').trim();
|
||||||
const firstPart = parts[0].trim();
|
currentChar = {
|
||||||
const emojiMatch = firstPart.match(/^(.+?):\s*(.+)$/);
|
name,
|
||||||
|
emoji: '😊', // Default emoji
|
||||||
|
traits: '',
|
||||||
|
relationship: 'Neutral',
|
||||||
|
thoughts: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Details line: "Details: 🧐 | Trait1, Trait2 | More traits"
|
||||||
|
else if (trimmed.startsWith('Details:') && currentChar) {
|
||||||
|
const detailsText = trimmed.replace('Details:', '').trim();
|
||||||
|
const parts = detailsText.split('|').map(p => p.trim());
|
||||||
|
|
||||||
if (emojiMatch) {
|
// First part is emoji
|
||||||
const emoji = emojiMatch[1].trim();
|
if (parts[0]) {
|
||||||
const info = emojiMatch[2].trim();
|
currentChar.emoji = parts[0];
|
||||||
|
}
|
||||||
|
|
||||||
let relationship, thoughts, traits;
|
// Remaining parts are traits
|
||||||
|
if (parts.length > 1) {
|
||||||
|
currentChar.traits = parts.slice(1).join(', ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Relationship line: "Relationship: Ally (details)"
|
||||||
|
else if (trimmed.startsWith('Relationship:') && currentChar) {
|
||||||
|
currentChar.relationship = trimmed.replace('Relationship:', '').trim();
|
||||||
|
}
|
||||||
|
// Thoughts line: "Thoughts: ..."
|
||||||
|
else if (trimmed.startsWith('Thoughts:') && currentChar) {
|
||||||
|
currentChar.thoughts = trimmed.replace('Thoughts:', '').trim()
|
||||||
|
.replace(/^["']|["']$/g, ''); // Remove surrounding quotes
|
||||||
|
}
|
||||||
|
// Stats line: "Stats: ..." (optional, currently ignored but could be stored)
|
||||||
|
else if (trimmed.startsWith('Stats:') && currentChar) {
|
||||||
|
// Optional: could parse and store stats if needed
|
||||||
|
// For now, we'll skip it as the widget doesn't display character stats
|
||||||
|
}
|
||||||
|
// Legacy single-line format fallback: "🧐: Name, Traits | Relationship | Thoughts"
|
||||||
|
else if (trimmed.includes('|') && !currentChar) {
|
||||||
|
const parts = trimmed.split('|').map(p => p.trim());
|
||||||
|
|
||||||
if (parts.length === 3) {
|
if (parts.length >= 3) {
|
||||||
// 3-part format
|
const firstPart = parts[0].trim();
|
||||||
relationship = parts[1].trim();
|
const emojiMatch = firstPart.match(/^(.+?):\s*(.+)$/);
|
||||||
thoughts = parts[2].trim();
|
|
||||||
|
if (emojiMatch) {
|
||||||
|
const emoji = emojiMatch[1].trim();
|
||||||
|
const info = emojiMatch[2].trim();
|
||||||
const infoParts = info.split(',').map(p => p.trim());
|
const infoParts = info.split(',').map(p => p.trim());
|
||||||
traits = infoParts.slice(1).join(', ');
|
const name = infoParts[0] || '';
|
||||||
} else {
|
const traits = infoParts.slice(1).join(', ');
|
||||||
// 4-part format (includes demeanor)
|
const relationship = parts[1].trim();
|
||||||
const demeanor = parts[1].trim();
|
const thoughts = parts[2].trim();
|
||||||
relationship = parts[2].trim();
|
|
||||||
thoughts = parts[3].trim();
|
|
||||||
const infoParts = info.split(',').map(p => p.trim());
|
|
||||||
const baseTraits = infoParts.slice(1).join(', ');
|
|
||||||
traits = baseTraits ? `${baseTraits}, ${demeanor}` : demeanor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse name (first part before comma)
|
if (name && name.toLowerCase() !== 'unavailable') {
|
||||||
const infoParts = info.split(',').map(p => p.trim());
|
presentCharacters.push({ emoji, name, traits, relationship, thoughts });
|
||||||
const name = infoParts[0] || '';
|
}
|
||||||
|
|
||||||
if (name && name.toLowerCase() !== 'unavailable') {
|
|
||||||
presentCharacters.push({ emoji, name, traits, relationship, thoughts });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save last character
|
||||||
|
if (currentChar && currentChar.name && currentChar.name.toLowerCase() !== 'unavailable') {
|
||||||
|
presentCharacters.push(currentChar);
|
||||||
|
}
|
||||||
|
|
||||||
return presentCharacters;
|
return presentCharacters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user