v3.1.0: Add parser error detection and recommended models section

This commit is contained in:
Spicy_Marinara
2026-01-07 22:56:26 +01:00
parent dbf5c2d17a
commit a3063aff4f
33 changed files with 599 additions and 459 deletions
+10 -24
View File
@@ -7,34 +7,20 @@ An immersive RPG extension for browsers that tracks character stats, scene infor
## 🆕 What's New ## 🆕 What's New
### v3.1.0
- Added toastr notification when the model returns an incorrect tracker format that cannot be parsed.
- Added guidance in settings about which models work best with the extension (I'm tired of people asking me why this doesn't work with their Q4 MythoMax 13B).
- Fixed auto-update after messages to work with External API generation mode.
- Fixed mobile UI.
- Fixed a minor bug with the thought bubble icon appearing in the wrong place on mobiles.
### v3.0.1 ### v3.0.1
- Small bug fix where you couldn't edit the thought bubble. - Small bug fix where you couldn't edit the thought bubble.
### v3.0.0
**What's new?**
- Switched to the JSON format for the trackers.
- You can now lock/unlock trackers that you don't want the model to change between generations.
- Removed features that were half-baked or didn't work.
- Organized Settings and Edit Trackers windows.
- All features of the extension are now accessible from the main panel view.
- Added Colored Dialogues option that makes the model color dialogue lines differently depending on the speaker.
- Introduced Dynamic Weather Effects that add visual effects to your SillyTavern window depending on the current weather from the trackers.
- All prompts used for the extension's features are now editable.
- Made the user's level optional in the Edit Trackers.
**Bug Fixes:**
- Fixed tracker logic in Together generation mode.
- Fixed various UI bugs (too many to count).
- Upgraded mobile view.
- Spotify Music widget is more visible now, plus it works in the mobile view.
- Auto-update after messages option is now available for External API generation mode.
- Fixed the display of the thoughts window and its mobile display.
- Fixed smaller bugs.
**Special thanks to all the other contributors for this project:** **Special thanks to all the other contributors for this project:**
Paperboygold, Munimunigamer, Subarashimo, Lilminzyu, Claude, IDeathByte, Chungchandev, Joenunezb, and Amauragis! Paperboygold, Munimunigamer, Subarashimo, Lilminzyu, Claude, IDeathByte, Chungchandev, Joenunezb, and Amauragis.
## 📥 Installation ## 📥 Installation
@@ -287,7 +273,7 @@ If you enjoy this extension, consider supporting development:
## 🙏 Credits ## 🙏 Credits
**Contributors:** **Contributors:**
SpicyMarinara, Paperboygold, Munimunigamer, Subarashimo, Lilminzyu, Claude, IDeathByte, Chungchandev, Joenunezb, and Amauragis SpicyMarinara, Paperboygold, Munimunigamer, Subarashimo, Lilminzyu, Claude, IDeathByte, Chungchandev, Joenunezb, and Amauragis.
## 🚀 Planned Features ## 🚀 Planned Features
+7 -7
View File
@@ -253,7 +253,7 @@ async function initUI() {
// Only initialize UI if extension is enabled // Only initialize UI if extension is enabled
if (!extensionSettings.enabled) { if (!extensionSettings.enabled) {
console.log('[RPG Companion] Extension disabled - skipping UI initialization'); // console.log('[RPG Companion] Extension disabled - skipping UI initialization');
return; return;
} }
@@ -941,11 +941,11 @@ jQuery(async () => {
// Check if migration to v3 JSON format is needed // Check if migration to v3 JSON format is needed
try { try {
if (extensionSettings.settingsVersion < 3) { if (extensionSettings.settingsVersion < 3) {
console.log('[RPG Companion] Detected v2 format, migrating to v3 JSON...'); // console.log('[RPG Companion] Detected v2 format, migrating to v3 JSON...');
await migrateToV3JSON(); await migrateToV3JSON();
updateExtensionSettings({ settingsVersion: 3 }); updateExtensionSettings({ settingsVersion: 3 });
await saveSettings(); await saveSettings();
console.log('[RPG Companion] ✅ Migration to v3 complete'); // console.log('[RPG Companion] ✅ Migration to v3 complete');
} }
} catch (error) { } catch (error) {
console.error('[RPG Companion] Migration to v3 failed:', error); console.error('[RPG Companion] Migration to v3 failed:', error);
@@ -1014,9 +1014,9 @@ jQuery(async () => {
try { try {
const conflicts = detectConflictingRegexScripts(st_extension_settings); const conflicts = detectConflictingRegexScripts(st_extension_settings);
if (conflicts.length > 0) { if (conflicts.length > 0) {
console.log('[RPG Companion] ⚠️ Detected old manual formatting regex scripts that may conflict:'); // console.log('[RPG Companion] ⚠️ Detected old manual formatting regex scripts that may conflict:');
conflicts.forEach(name => console.log(` - ${name}`)); // conflicts.forEach(name => console.log(` - ${name}`));
console.log('[RPG Companion] Consider disabling these regexes as the extension now handles formatting automatically.'); // console.log('[RPG Companion] Consider disabling these regexes as the extension now handles formatting automatically.');
// Show user-friendly warning (non-blocking) // Show user-friendly warning (non-blocking)
// toastr.warning( // toastr.warning(
@@ -1067,7 +1067,7 @@ jQuery(async () => {
// Non-critical - continue without it // Non-critical - continue without it
} }
console.log('[RPG Companion] ✅ Extension loaded successfully'); // console.log('[RPG Companion] ✅ Extension loaded successfully');
} catch (error) { } catch (error) {
console.error('[RPG Companion] ❌ Critical initialization failure:', error); console.error('[RPG Companion] ❌ Critical initialization failure:', error);
console.error('[RPG Companion] Error details:', error.message, error.stack); console.error('[RPG Companion] Error details:', error.message, error.stack);
+1 -1
View File
@@ -6,6 +6,6 @@
"js": "index.js", "js": "index.js",
"css": "style.css", "css": "style.css",
"author": "Marinara", "author": "Marinara",
"version": "3.0.1", "version": "3.1.0",
"homePage": "https://github.com/SpicyMarinara/rpg-companion-sillytavern" "homePage": "https://github.com/SpicyMarinara/rpg-companion-sillytavern"
} }
+11 -2
View File
@@ -29,17 +29,26 @@
</a> </a>
</div> </div>
<div style="margin-top: 15px; text-align: center; opacity: 0.7; font-size: 0.8em; line-height: 1.5;">
<div style="margin-bottom: 5px;">
<i class="fa-solid fa-microchip"></i> <strong data-i18n="settings.recommendedModels.title">Recommended Models:</strong>
</div>
<div style="opacity: 0.8; font-size: 0.9em;" data-i18n="settings.recommendedModels.description">
For the extension to work properly, <strong>it is not recommended to use any models below 20B, especially if they're old.</strong> It works best with the SOTA models such as Deepseek, Claude, GPT, or Gemini.
</div>
</div>
<div style="margin-top: 15px; text-align: center; opacity: 0.7; font-size: 0.8em; line-height: 1.5;"> <div style="margin-top: 15px; text-align: center; opacity: 0.7; font-size: 0.8em; line-height: 1.5;">
<div style="margin-bottom: 5px;"> <div style="margin-bottom: 5px;">
<i class="fa-solid fa-users"></i> <strong>Contributors:</strong> <i class="fa-solid fa-users"></i> <strong>Contributors:</strong>
</div> </div>
<div style="opacity: 0.8; font-size: 0.9em;"> <div style="opacity: 0.8; font-size: 0.9em;">
SpicyMarinara, Paperboygold, Munimunigamer, Subarashimo, Lilminzyu, Claude, IDeathByte, Chungchandev, Joenunezb, and Amauragis SpicyMarinara, Paperboygold, Munimunigamer, Subarashimo, Lilminzyu, Claude, IDeathByte, Chungchandev, Joenunezb, and Amauragis.
</div> </div>
</div> </div>
<div style="margin-top: 10px; text-align: center; opacity: 0.6; font-size: 0.85em;"> <div style="margin-top: 10px; text-align: center; opacity: 0.6; font-size: 0.85em;">
v3.0.1 v3.1.0
</div> </div>
</div> </div>
</div> </div>
+30 -30
View File
@@ -86,7 +86,7 @@ export function loadSettings() {
// Migration to version 2: Enable dynamic weather for existing users // Migration to version 2: Enable dynamic weather for existing users
if (currentVersion < 2) { if (currentVersion < 2) {
console.log('[RPG Companion] Migrating settings to version 2 (enabling dynamic weather)'); // console.log('[RPG Companion] Migrating settings to version 2 (enabling dynamic weather)');
extensionSettings.enableDynamicWeather = true; extensionSettings.enableDynamicWeather = true;
extensionSettings.settingsVersion = 2; extensionSettings.settingsVersion = 2;
settingsChanged = true; settingsChanged = true;
@@ -94,7 +94,7 @@ export function loadSettings() {
// Migration to version 3: Convert text trackers to JSON format // Migration to version 3: Convert text trackers to JSON format
if (currentVersion < 3) { if (currentVersion < 3) {
console.log('[RPG Companion] Migrating settings to version 3 (JSON tracker format)'); // console.log('[RPG Companion] Migrating settings to version 3 (JSON tracker format)');
migrateToV3JSON(); migrateToV3JSON();
extensionSettings.settingsVersion = 3; extensionSettings.settingsVersion = 3;
settingsChanged = true; settingsChanged = true;
@@ -114,7 +114,7 @@ export function loadSettings() {
if (FEATURE_FLAGS.useNewInventory) { if (FEATURE_FLAGS.useNewInventory) {
const migrationResult = migrateInventory(extensionSettings.userStats.inventory); const migrationResult = migrateInventory(extensionSettings.userStats.inventory);
if (migrationResult.migrated) { if (migrationResult.migrated) {
console.log(`[RPG Companion] Inventory migrated from ${migrationResult.source} to v2 format`); // console.log(`[RPG Companion] Inventory migrated from ${migrationResult.source} to v2 format`);
extensionSettings.userStats.inventory = migrationResult.inventory; extensionSettings.userStats.inventory = migrationResult.inventory;
saveSettings(); // Persist migrated inventory saveSettings(); // Persist migrated inventory
} }
@@ -122,7 +122,7 @@ export function loadSettings() {
// Migrate to trackerConfig if it doesn't exist // Migrate to trackerConfig if it doesn't exist
if (!extensionSettings.trackerConfig) { if (!extensionSettings.trackerConfig) {
console.log('[RPG Companion] Migrating to trackerConfig format'); // console.log('[RPG Companion] Migrating to trackerConfig format');
migrateToTrackerConfig(); migrateToTrackerConfig();
saveSettings(); // Persist migration saveSettings(); // Persist migration
} }
@@ -161,13 +161,13 @@ export function saveChatData() {
return; return;
} }
console.log('[RPG Companion] 💾 saveChatData called - committedTrackerData:', { // console.log('[RPG Companion] 💾 saveChatData called - committedTrackerData:', {
userStats: committedTrackerData.userStats ? `${committedTrackerData.userStats.substring(0, 50)}...` : 'null', // userStats: committedTrackerData.userStats ? `${committedTrackerData.userStats.substring(0, 50)}...` : 'null',
infoBox: committedTrackerData.infoBox ? 'exists' : 'null', // infoBox: committedTrackerData.infoBox ? 'exists' : 'null',
characterThoughts: committedTrackerData.characterThoughts ? 'exists' : 'null' // characterThoughts: committedTrackerData.characterThoughts ? 'exists' : 'null'
}); // });
console.log('[RPG Companion] 💾 saveChatData RAW committedTrackerData:', committedTrackerData); // console.log('[RPG Companion] 💾 saveChatData RAW committedTrackerData:', committedTrackerData);
console.log('[RPG Companion] 💾 saveChatData RAW lastGeneratedData:', lastGeneratedData); // console.log('[RPG Companion] 💾 saveChatData RAW lastGeneratedData:', lastGeneratedData);
chat_metadata.rpg_companion = { chat_metadata.rpg_companion = {
userStats: extensionSettings.userStats, userStats: extensionSettings.userStats,
@@ -284,34 +284,34 @@ export function loadChatData() {
// Restore committed tracker data first // Restore committed tracker data first
if (savedData.committedTrackerData) { if (savedData.committedTrackerData) {
console.log('[RPG Companion] 📥 loadChatData restoring committedTrackerData:', { // console.log('[RPG Companion] 📥 loadChatData restoring committedTrackerData:', {
userStats: savedData.committedTrackerData.userStats ? `${savedData.committedTrackerData.userStats.substring(0, 50)}...` : 'null', // userStats: savedData.committedTrackerData.userStats ? `${savedData.committedTrackerData.userStats.substring(0, 50)}...` : 'null',
infoBox: savedData.committedTrackerData.infoBox ? 'exists' : 'null', // infoBox: savedData.committedTrackerData.infoBox ? 'exists' : 'null',
characterThoughts: savedData.committedTrackerData.characterThoughts ? 'exists' : 'null' // characterThoughts: savedData.committedTrackerData.characterThoughts ? 'exists' : 'null'
}); // });
console.log('[RPG Companion] 📥 RAW savedData.committedTrackerData:', savedData.committedTrackerData); // console.log('[RPG Companion] 📥 RAW savedData.committedTrackerData:', savedData.committedTrackerData);
console.log('[RPG Companion] 📥 Type check:', { // console.log('[RPG Companion] 📥 Type check:', {
userStatsType: typeof savedData.committedTrackerData.userStats, // userStatsType: typeof savedData.committedTrackerData.userStats,
infoBoxType: typeof savedData.committedTrackerData.infoBox, // infoBoxType: typeof savedData.committedTrackerData.infoBox,
characterThoughtsType: typeof savedData.committedTrackerData.characterThoughts // characterThoughtsType: typeof savedData.committedTrackerData.characterThoughts
}); // });
setCommittedTrackerData({ ...savedData.committedTrackerData }); setCommittedTrackerData({ ...savedData.committedTrackerData });
} }
// Restore last generated data (for display) // Restore last generated data (for display)
// Always prefer lastGeneratedData as it contains the most recent generation (including swipes) // Always prefer lastGeneratedData as it contains the most recent generation (including swipes)
if (savedData.lastGeneratedData) { if (savedData.lastGeneratedData) {
console.log('[RPG Companion] 📥 loadChatData restoring lastGeneratedData'); // console.log('[RPG Companion] 📥 loadChatData restoring lastGeneratedData');
setLastGeneratedData({ ...savedData.lastGeneratedData }); setLastGeneratedData({ ...savedData.lastGeneratedData });
} else { } else {
console.log('[RPG Companion] ⚠️ No lastGeneratedData found in save'); // console.log('[RPG Companion] ⚠️ No lastGeneratedData found in save');
} }
// Migrate inventory in chat data if feature flag enabled // Migrate inventory in chat data if feature flag enabled
if (FEATURE_FLAGS.useNewInventory && extensionSettings.userStats.inventory) { if (FEATURE_FLAGS.useNewInventory && extensionSettings.userStats.inventory) {
const migrationResult = migrateInventory(extensionSettings.userStats.inventory); const migrationResult = migrateInventory(extensionSettings.userStats.inventory);
if (migrationResult.migrated) { if (migrationResult.migrated) {
console.log(`[RPG Companion] Chat inventory migrated from ${migrationResult.source} to v2 format`); // console.log(`[RPG Companion] Chat inventory migrated from ${migrationResult.source} to v2 format`);
extensionSettings.userStats.inventory = migrationResult.inventory; extensionSettings.userStats.inventory = migrationResult.inventory;
saveChatData(); // Persist migrated inventory to chat metadata saveChatData(); // Persist migrated inventory to chat metadata
} }
@@ -401,7 +401,7 @@ function validateInventoryStructure(inventory, source) {
// Persist repairs if needed // Persist repairs if needed
if (needsSave) { if (needsSave) {
console.log(`[RPG Companion] Repaired inventory structure from ${source}, saving...`); // console.log(`[RPG Companion] Repaired inventory structure from ${source}, saving...`);
saveSettings(); saveSettings();
if (source === 'chat') { if (source === 'chat') {
saveChatData(); saveChatData();
@@ -473,7 +473,7 @@ function migrateToTrackerConfig() {
name: extensionSettings.statNames[id] || id.charAt(0).toUpperCase() + id.slice(1), name: extensionSettings.statNames[id] || id.charAt(0).toUpperCase() + id.slice(1),
enabled: true enabled: true
})); }));
console.log('[RPG Companion] Migrated statNames to customStats array'); // console.log('[RPG Companion] Migrated statNames to customStats array');
} }
// Ensure all stats have corresponding values in userStats // Ensure all stats have corresponding values in userStats
@@ -497,7 +497,7 @@ function migrateToTrackerConfig() {
{ id: 'cha', name: 'CHA', enabled: shouldShow } { id: 'cha', name: 'CHA', enabled: shouldShow }
]; ];
delete extensionSettings.trackerConfig.userStats.showRPGAttributes; delete extensionSettings.trackerConfig.userStats.showRPGAttributes;
console.log('[RPG Companion] Migrated showRPGAttributes to rpgAttributes array'); // console.log('[RPG Companion] Migrated showRPGAttributes to rpgAttributes array');
} }
// Ensure rpgAttributes exists even if no migration was needed // Ensure rpgAttributes exists even if no migration was needed
@@ -535,7 +535,7 @@ function migrateToTrackerConfig() {
const hasOldFormat = pc.customFields.some(f => f.label || f.placeholder || f.type === 'relationship'); const hasOldFormat = pc.customFields.some(f => f.label || f.placeholder || f.type === 'relationship');
if (hasOldFormat) { if (hasOldFormat) {
console.log('[RPG Companion] Migrating Present Characters to new structure'); // console.log('[RPG Companion] Migrating Present Characters to new structure');
// Extract relationship fields from old customFields // Extract relationship fields from old customFields
const relationshipFields = ['Lover', 'Friend', 'Ally', 'Enemy', 'Neutral']; const relationshipFields = ['Lover', 'Friend', 'Ally', 'Enemy', 'Neutral'];
@@ -563,7 +563,7 @@ function migrateToTrackerConfig() {
pc.customFields = newCustomFields; pc.customFields = newCustomFields;
pc.thoughts = thoughts; pc.thoughts = thoughts;
console.log('[RPG Companion] Present Characters migration complete'); // console.log('[RPG Companion] Present Characters migration complete');
saveSettings(); // Persist the migration saveSettings(); // Persist the migration
} }
} }
+13 -13
View File
@@ -376,24 +376,24 @@ export function updateLastGeneratedData(updates) {
} }
export function setCommittedTrackerData(data) { export function setCommittedTrackerData(data) {
console.log('[RPG State] setCommittedTrackerData called with:', data); // console.log('[RPG State] setCommittedTrackerData called with:', data);
console.log('[RPG State] Type check on input:', { // console.log('[RPG State] Type check on input:', {
userStatsType: typeof data.userStats, // userStatsType: typeof data.userStats,
infoBoxType: typeof data.infoBox, // infoBoxType: typeof data.infoBox,
characterThoughtsType: typeof data.characterThoughts, // characterThoughtsType: typeof data.characterThoughts,
userStatsValue: data.userStats, // userStatsValue: data.userStats,
infoBoxValue: data.infoBox, // infoBoxValue: data.infoBox,
characterThoughtsValue: data.characterThoughts // characterThoughtsValue: data.characterThoughts
}); // });
committedTrackerData = data; committedTrackerData = data;
console.log('[RPG State] committedTrackerData after assignment:', committedTrackerData); // console.log('[RPG State] committedTrackerData after assignment:', committedTrackerData);
} }
export function updateCommittedTrackerData(updates) { export function updateCommittedTrackerData(updates) {
console.log('[RPG State] updateCommittedTrackerData called with:', updates); // console.log('[RPG State] updateCommittedTrackerData called with:', updates);
console.log('[RPG State] committedTrackerData before update:', committedTrackerData); // console.log('[RPG State] committedTrackerData before update:', committedTrackerData);
Object.assign(committedTrackerData, updates); Object.assign(committedTrackerData, updates);
console.log('[RPG State] committedTrackerData after update:', committedTrackerData); // console.log('[RPG State] committedTrackerData after update:', committedTrackerData);
} }
export function setLastActionWasSwipe(value) { export function setLastActionWasSwipe(value) {
+4 -1
View File
@@ -230,5 +230,8 @@
"checkpoint.indicator": "Chapter Start", "checkpoint.indicator": "Chapter Start",
"checkpoint.tooltip": "Messages before this point are excluded from context", "checkpoint.tooltip": "Messages before this point are excluded from context",
"musicPlayer.title": "Scene Music", "musicPlayer.title": "Scene Music",
"musicPlayer.noMusic": "AI will suggest music when appropriate for the scene" "musicPlayer.noMusic": "AI will suggest music when appropriate for the scene",
"errors.parsingError": "RPG Companion Trackers' parsing error! The model returned an incorrect format. If the issue persists, consider changing the model for generations.",
"settings.recommendedModels.title": "Recommended Models",
"settings.recommendedModels.description": "For the extension to work properly, **it is not recommended to use any models below 20B, especially if they're old.** It works best with the SOTA models such as Deepseek, Claude, GPT, or Gemini."
} }
+4 -1
View File
@@ -193,5 +193,8 @@
"quests.optional.empty": "當前無支線任務 (ʘ̆ʚʘ̆)", "quests.optional.empty": "當前無支線任務 (ʘ̆ʚʘ̆)",
"quests.optional.hint": "支線任務是補充主線劇情的支線目標。", "quests.optional.hint": "支線任務是補充主線劇情的支線目標。",
"musicPlayer.title": "場景音樂", "musicPlayer.title": "場景音樂",
"musicPlayer.noMusic": "AI 會在適當時為場景建議音樂" "musicPlayer.noMusic": "AI 會在適當時為場景建議音樂",
"errors.parsingError": "RPG Companion 追蹤器解析錯誤!模型返回了不正確的格式。如果問題持續存在,請考慮更換生成模型。",
"settings.recommendedModels.title": "推薦模型",
"settings.recommendedModels.description": "為了讓擴充功能正常運作,**不建議使用任何小於 20B 的模型,尤其是舊模型。**它最適合使用 SOTA 模型,例如 Deepseek、Claude、GPT 或 Gemini。"
} }
+9 -9
View File
@@ -147,7 +147,7 @@ export async function generateAvatarsForCharacters(characterNames, onStarted = n
return; return;
} }
console.log('[RPG Avatar] Starting batch generation for:', needsGeneration); // console.log('[RPG Avatar] Starting batch generation for:', needsGeneration);
// Mark all as pending IMMEDIATELY (before any async work) // Mark all as pending IMMEDIATELY (before any async work)
for (const name of needsGeneration) { for (const name of needsGeneration) {
@@ -192,7 +192,7 @@ export async function generateAvatarsForCharacters(characterNames, onStarted = n
} }
} }
console.log('[RPG Avatar] Batch generation complete'); // console.log('[RPG Avatar] Batch generation complete');
} }
/** /**
@@ -204,7 +204,7 @@ export async function generateAvatarsForCharacters(characterNames, onStarted = n
* @returns {Promise<string|null>} New avatar URL or null if failed * @returns {Promise<string|null>} New avatar URL or null if failed
*/ */
export async function regenerateAvatar(characterName) { export async function regenerateAvatar(characterName) {
console.log('[RPG Avatar] Regenerating avatar for:', characterName); // console.log('[RPG Avatar] Regenerating avatar for:', characterName);
// Mark as pending immediately // Mark as pending immediately
pendingGenerations.add(characterName); pendingGenerations.add(characterName);
@@ -245,13 +245,13 @@ async function generateAvatarPrompt(characterName) {
} }
try { try {
console.log('[RPG Avatar] Generating LLM prompt for:', characterName); // console.log('[RPG Avatar] Generating LLM prompt for:', characterName);
const promptMessages = await generateAvatarPromptGenerationPrompt(characterName); const promptMessages = await generateAvatarPromptGenerationPrompt(characterName);
let response; let response;
if (extensionSettings.generationMode === 'external') { if (extensionSettings.generationMode === 'external') {
console.log('[RPG Avatar] Using external API for avatar prompt generation'); // console.log('[RPG Avatar] Using external API for avatar prompt generation');
response = await generateWithExternalAPI(promptMessages); response = await generateWithExternalAPI(promptMessages);
} else { } else {
response = await generateRaw({ response = await generateRaw({
@@ -262,7 +262,7 @@ async function generateAvatarPrompt(characterName) {
if (response) { if (response) {
const prompt = response.trim(); const prompt = response.trim();
console.log(`[RPG Avatar] Generated prompt for ${characterName}:`, prompt); // console.log(`[RPG Avatar] Generated prompt for ${characterName}:`, prompt);
// Store prompt in session storage // Store prompt in session storage
setSessionAvatarPrompt(characterName, prompt); setSessionAvatarPrompt(characterName, prompt);
@@ -313,11 +313,11 @@ async function generateSingleAvatar(characterName, prompt = null) {
} }
if (!prompt) { if (!prompt) {
console.log(`[RPG Avatar] No LLM prompt for ${characterName}, using fallback prompt`); // console.log(`[RPG Avatar] No LLM prompt for ${characterName}, using fallback prompt`);
prompt = buildFallbackPrompt(characterName); prompt = buildFallbackPrompt(characterName);
} }
console.log(`[RPG Avatar] Starting image generation for: ${characterName}`); // console.log(`[RPG Avatar] Starting image generation for: ${characterName}`);
try { try {
// Execute /sd command with quiet=true to suppress chat output // Execute /sd command with quiet=true to suppress chat output
@@ -337,7 +337,7 @@ async function generateSingleAvatar(characterName, prompt = null) {
extensionSettings.npcAvatars[characterName] = imageUrl; extensionSettings.npcAvatars[characterName] = imageUrl;
saveSettings(); saveSettings();
console.log(`[RPG Avatar] Successfully generated avatar for: ${characterName}`); // console.log(`[RPG Avatar] Successfully generated avatar for: ${characterName}`);
return imageUrl; return imageUrl;
} else { } else {
console.warn(`[RPG Avatar] Failed to extract image URL for ${characterName}:`, result); console.warn(`[RPG Avatar] Failed to extract image URL for ${characterName}:`, result);
+7 -7
View File
@@ -49,7 +49,7 @@ export async function setChapterCheckpoint(messageId) {
if (previousCheckpoint !== null && previousCheckpoint !== undefined && previousCheckpoint !== messageId && currentlyHiddenRange !== null) { if (previousCheckpoint !== null && previousCheckpoint !== undefined && previousCheckpoint !== messageId && currentlyHiddenRange !== null) {
const { start, end } = currentlyHiddenRange; const { start, end } = currentlyHiddenRange;
await executeSlashCommandsOnChatInput(`/unhide ${start}-${end}`, { quiet: true }); await executeSlashCommandsOnChatInput(`/unhide ${start}-${end}`, { quiet: true });
console.log(`[RPG Companion] Unhid previous range: ${start}-${end}`); // console.log(`[RPG Companion] Unhid previous range: ${start}-${end}`);
} }
// Store in chat metadata (this automatically overrides any previous checkpoint) // Store in chat metadata (this automatically overrides any previous checkpoint)
@@ -61,13 +61,13 @@ export async function setChapterCheckpoint(messageId) {
const rangeEnd = messageId - 1; const rangeEnd = messageId - 1;
await executeSlashCommandsOnChatInput(`/hide 0-${rangeEnd}`, { quiet: true }); await executeSlashCommandsOnChatInput(`/hide 0-${rangeEnd}`, { quiet: true });
currentlyHiddenRange = { start: 0, end: rangeEnd }; currentlyHiddenRange = { start: 0, end: rangeEnd };
console.log(`[RPG Companion] Hidden messages 0-${rangeEnd} (checkpoint at ${messageId})`); // console.log(`[RPG Companion] Hidden messages 0-${rangeEnd} (checkpoint at ${messageId})`);
} }
if (previousCheckpoint !== null && previousCheckpoint !== undefined && previousCheckpoint !== messageId) { if (previousCheckpoint !== null && previousCheckpoint !== undefined && previousCheckpoint !== messageId) {
console.log(`[RPG Companion] Chapter checkpoint moved from message ${previousCheckpoint} to ${messageId}`); // console.log(`[RPG Companion] Chapter checkpoint moved from message ${previousCheckpoint} to ${messageId}`);
} else { } else {
console.log('[RPG Companion] Chapter checkpoint set at message', messageId); // console.log('[RPG Companion] Chapter checkpoint set at message', messageId);
} }
// Emit event for UI updates // Emit event for UI updates
@@ -91,14 +91,14 @@ export async function clearChapterCheckpoint() {
if (currentlyHiddenRange !== null) { if (currentlyHiddenRange !== null) {
const { start, end } = currentlyHiddenRange; const { start, end } = currentlyHiddenRange;
await executeSlashCommandsOnChatInput(`/unhide ${start}-${end}`, { quiet: true }); await executeSlashCommandsOnChatInput(`/unhide ${start}-${end}`, { quiet: true });
console.log(`[RPG Companion] Unhid messages ${start}-${end}`); // console.log(`[RPG Companion] Unhid messages ${start}-${end}`);
currentlyHiddenRange = null; currentlyHiddenRange = null;
} }
delete chat_metadata.rpg_companion_chapter_checkpoint; delete chat_metadata.rpg_companion_chapter_checkpoint;
saveChatDebounced(); saveChatDebounced();
console.log('[RPG Companion] Chapter checkpoint cleared'); // console.log('[RPG Companion] Chapter checkpoint cleared');
// Emit event for UI updates // Emit event for UI updates
if (typeof document !== 'undefined') { if (typeof document !== 'undefined') {
@@ -173,7 +173,7 @@ export async function restoreCheckpointOnLoad() {
if (needsRestore) { if (needsRestore) {
await executeSlashCommandsOnChatInput(`/hide 0-${rangeEnd}`, { quiet: true }); await executeSlashCommandsOnChatInput(`/hide 0-${rangeEnd}`, { quiet: true });
currentlyHiddenRange = { start: 0, end: rangeEnd }; currentlyHiddenRange = { start: 0, end: rangeEnd };
console.log(`[RPG Companion] Restored checkpoint: Hidden messages 0-${rangeEnd}`); // console.log(`[RPG Companion] Restored checkpoint: Hidden messages 0-${rangeEnd}`);
} else { } else {
currentlyHiddenRange = { start: 0, end: rangeEnd }; currentlyHiddenRange = { start: 0, end: rangeEnd };
} }
+4 -4
View File
@@ -63,7 +63,7 @@ export async function ensureHtmlCleaningRegex(st_extension_settings, saveSetting
); );
if (alreadyExists) { if (alreadyExists) {
console.log('[RPG Companion] HTML cleaning regex already exists, skipping import'); // console.log('[RPG Companion] HTML cleaning regex already exists, skipping import');
return; return;
} }
@@ -107,7 +107,7 @@ export async function ensureHtmlCleaningRegex(st_extension_settings, saveSetting
console.warn('[RPG Companion] saveSettingsDebounced is not a function, cannot save HTML regex'); console.warn('[RPG Companion] saveSettingsDebounced is not a function, cannot save HTML regex');
} }
console.log('[RPG Companion] ✅ HTML cleaning regex imported successfully'); // console.log('[RPG Companion] ✅ HTML cleaning regex imported successfully');
} catch (error) { } catch (error) {
console.error('[RPG Companion] Failed to import HTML cleaning regex:', error); console.error('[RPG Companion] Failed to import HTML cleaning regex:', error);
console.error('[RPG Companion] Error details:', error.message, error.stack); console.error('[RPG Companion] Error details:', error.message, error.stack);
@@ -145,7 +145,7 @@ export async function ensureTrackerCleaningRegex(st_extension_settings, saveSett
); );
if (alreadyExists) { if (alreadyExists) {
console.log('[RPG Companion] Tracker cleaning regex already exists, skipping import'); // console.log('[RPG Companion] Tracker cleaning regex already exists, skipping import');
return; return;
} }
@@ -190,7 +190,7 @@ export async function ensureTrackerCleaningRegex(st_extension_settings, saveSett
console.warn('[RPG Companion] saveSettingsDebounced is not a function, cannot save tracker cleaning regex'); console.warn('[RPG Companion] saveSettingsDebounced is not a function, cannot save tracker cleaning regex');
} }
console.log('[RPG Companion] ✅ Tracker cleaning regex imported successfully'); // console.log('[RPG Companion] ✅ Tracker cleaning regex imported successfully');
} catch (error) { } catch (error) {
console.error('[RPG Companion] Failed to import tracker cleaning regex:', error); console.error('[RPG Companion] Failed to import tracker cleaning regex:', error);
console.error('[RPG Companion] Error details:', error.message, error.stack); console.error('[RPG Companion] Error details:', error.message, error.stack);
+5 -5
View File
@@ -32,11 +32,11 @@ export async function ensureJsonCleaningRegex(st_extension_settings, saveSetting
); );
if (alreadyExists) { if (alreadyExists) {
console.log('[RPG Companion] JSON cleaning regex already exists, skipping import'); // console.log('[RPG Companion] JSON cleaning regex already exists, skipping import');
return; return;
} }
console.log('[RPG Companion] Importing JSON cleaning regex for Together mode...'); // console.log('[RPG Companion] Importing JSON cleaning regex for Together mode...');
// Generate a UUID for the script // Generate a UUID for the script
const uuidv4 = () => { const uuidv4 = () => {
@@ -82,8 +82,8 @@ export async function ensureJsonCleaningRegex(st_extension_settings, saveSetting
console.warn('[RPG Companion] saveSettingsDebounced is not a function, cannot save JSON cleaning regex'); console.warn('[RPG Companion] saveSettingsDebounced is not a function, cannot save JSON cleaning regex');
} }
console.log('[RPG Companion] ✅ JSON cleaning regex imported successfully'); // console.log('[RPG Companion] ✅ JSON cleaning regex imported successfully');
console.log('[RPG Companion] This regex will automatically remove tracker JSON from Together mode messages'); // console.log('[RPG Companion] This regex will automatically remove tracker JSON from Together mode messages');
} catch (error) { } catch (error) {
console.error('[RPG Companion] Failed to import JSON cleaning regex:', error); console.error('[RPG Companion] Failed to import JSON cleaning regex:', error);
console.error('[RPG Companion] Error details:', error.message, error.stack); console.error('[RPG Companion] Error details:', error.message, error.stack);
@@ -111,7 +111,7 @@ export function removeJsonCleaningRegex(st_extension_settings, saveSettingsDebou
); );
if (st_extension_settings.regex.length < initialLength) { if (st_extension_settings.regex.length < initialLength) {
console.log('[RPG Companion] Removed JSON cleaning regex'); // console.log('[RPG Companion] Removed JSON cleaning regex');
if (typeof saveSettingsDebounced === 'function') { if (typeof saveSettingsDebounced === 'function') {
saveSettingsDebounced(); saveSettingsDebounced();
} }
+2 -2
View File
@@ -52,11 +52,11 @@ export function parseAndStoreSpotifyUrl(responseText) {
if (!extensionSettings.enableSpotifyMusic) return false; if (!extensionSettings.enableSpotifyMusic) return false;
const songData = extractSpotifyUrl(responseText); const songData = extractSpotifyUrl(responseText);
console.log('[RPG Companion] Spotify Parser: Found song:', songData); // console.log('[RPG Companion] Spotify Parser: Found song:', songData);
if (songData) { if (songData) {
// Store in committed tracker data // Store in committed tracker data
committedTrackerData.spotifyUrl = songData; committedTrackerData.spotifyUrl = songData;
console.log('[RPG Companion] Spotify Parser: Stored song in committedTrackerData:', committedTrackerData.spotifyUrl); // console.log('[RPG Companion] Spotify Parser: Stored song in committedTrackerData:', committedTrackerData.spotifyUrl);
return true; return true;
} }
+11 -6
View File
@@ -62,7 +62,7 @@ export async function generateWithExternalAPI(messages) {
const normalizedBaseUrl = baseUrl.trim().replace(/\/+$/, ''); const normalizedBaseUrl = baseUrl.trim().replace(/\/+$/, '');
const endpoint = `${normalizedBaseUrl}/chat/completions`; const endpoint = `${normalizedBaseUrl}/chat/completions`;
console.log(`[RPG Companion] Calling external API: ${normalizedBaseUrl} with model: ${model}`); // console.log(`[RPG Companion] Calling external API: ${normalizedBaseUrl} with model: ${model}`);
try { try {
const response = await fetch(endpoint, { const response = await fetch(endpoint, {
@@ -103,7 +103,7 @@ export async function generateWithExternalAPI(messages) {
} }
const content = data.choices[0].message.content; const content = data.choices[0].message.content;
console.log('[RPG Companion] External API response received successfully'); // console.log('[RPG Companion] External API response received successfully');
return content; return content;
} catch (error) { } catch (error) {
@@ -242,7 +242,7 @@ export async function updateRPGData(renderUserStats, renderInfoBox, renderThough
let response; let response;
if (isExternalMode) { if (isExternalMode) {
// External mode: Use external OpenAI-compatible API directly // External mode: Use external OpenAI-compatible API directly
console.log('[RPG Companion] Using external API for tracker generation'); // console.log('[RPG Companion] Using external API for tracker generation');
response = await generateWithExternalAPI(prompt); response = await generateWithExternalAPI(prompt);
} else { } else {
// Separate mode: Use SillyTavern's generateRaw // Separate mode: Use SillyTavern's generateRaw
@@ -256,6 +256,11 @@ export async function updateRPGData(renderUserStats, renderInfoBox, renderThough
// console.log('[RPG Companion] Raw AI response:', response); // console.log('[RPG Companion] Raw AI response:', response);
const parsedData = parseResponse(response); const parsedData = parseResponse(response);
// Check if parsing completely failed (no tracker data found)
if (parsedData.parsingFailed) {
toastr.error(i18n.getTranslation('errors.parsingError'), '', { timeOut: 5000 });
}
// Remove locks from parsed data (JSON format only, text format is unaffected) // Remove locks from parsed data (JSON format only, text format is unaffected)
if (parsedData.userStats) { if (parsedData.userStats) {
parsedData.userStats = removeLocks(parsedData.userStats); parsedData.userStats = removeLocks(parsedData.userStats);
@@ -358,17 +363,17 @@ export async function updateRPGData(renderUserStats, renderInfoBox, renderThough
if (extensionSettings.autoGenerateAvatars) { if (extensionSettings.autoGenerateAvatars) {
const charactersNeedingAvatars = parseCharactersFromThoughts(parsedData.characterThoughts); const charactersNeedingAvatars = parseCharactersFromThoughts(parsedData.characterThoughts);
if (charactersNeedingAvatars.length > 0) { if (charactersNeedingAvatars.length > 0) {
console.log('[RPG Companion] Generating avatars for:', charactersNeedingAvatars); // console.log('[RPG Companion] Generating avatars for:', charactersNeedingAvatars);
// Generate avatars - this awaits completion // Generate avatars - this awaits completion
await generateAvatarsForCharacters(charactersNeedingAvatars, (names) => { await generateAvatarsForCharacters(charactersNeedingAvatars, (names) => {
// Callback when generation starts - re-render to show loading spinners // Callback when generation starts - re-render to show loading spinners
console.log('[RPG Companion] Avatar generation started, showing spinners...'); // console.log('[RPG Companion] Avatar generation started, showing spinners...');
renderThoughts(); renderThoughts();
}); });
// Re-render once all avatars are generated // Re-render once all avatars are generated
console.log('[RPG Companion] All avatars generated, re-rendering...'); // console.log('[RPG Companion] All avatars generated, re-rendering...');
renderThoughts(); renderThoughts();
} }
} }
+14 -14
View File
@@ -97,14 +97,14 @@ export async function buildEncounterInitPrompt() {
try { try {
// Debug logging // Debug logging
console.log('[RPG Companion] Checking world info:', { // console.log('[RPG Companion] Checking world info:', {
hasWindowGetWorldInfoPrompt: typeof window.getWorldInfoPrompt === 'function', // hasWindowGetWorldInfoPrompt: typeof window.getWorldInfoPrompt === 'function',
hasContextGetWorldInfoPrompt: typeof context.getWorldInfoPrompt === 'function', // hasContextGetWorldInfoPrompt: typeof context.getWorldInfoPrompt === 'function',
chatLength: chat?.length, // chatLength: chat?.length,
contextChatLength: context.chat?.length, // contextChatLength: context.chat?.length,
hasActivatedWorldInfo: !!context.activatedWorldInfo, // hasActivatedWorldInfo: !!context.activatedWorldInfo,
activatedWorldInfoLength: context.activatedWorldInfo?.length // activatedWorldInfoLength: context.activatedWorldInfo?.length
}); // });
// Use SillyTavern's getWorldInfoPrompt to get activated lorebook entries // Use SillyTavern's getWorldInfoPrompt to get activated lorebook entries
// Try context.getWorldInfoPrompt first, then window.getWorldInfoPrompt // Try context.getWorldInfoPrompt first, then window.getWorldInfoPrompt
@@ -114,20 +114,20 @@ export async function buildEncounterInitPrompt() {
if (typeof getWorldInfoFn === 'function' && currentChat && currentChat.length > 0) { if (typeof getWorldInfoFn === 'function' && currentChat && currentChat.length > 0) {
const chatForWI = currentChat.map(x => x.mes || x.message || x).filter(m => m && typeof m === 'string'); const chatForWI = currentChat.map(x => x.mes || x.message || x).filter(m => m && typeof m === 'string');
console.log('[RPG Companion] Calling getWorldInfoPrompt with', chatForWI.length, 'messages'); // console.log('[RPG Companion] Calling getWorldInfoPrompt with', chatForWI.length, 'messages');
const result = await getWorldInfoFn(chatForWI, 8000, false); const result = await getWorldInfoFn(chatForWI, 8000, false);
const worldInfoString = result?.worldInfoString || result; const worldInfoString = result?.worldInfoString || result;
console.log('[RPG Companion] World info result:', { worldInfoString, length: worldInfoString?.length }); // console.log('[RPG Companion] World info result:', { worldInfoString, length: worldInfoString?.length });
if (worldInfoString && worldInfoString.trim()) { if (worldInfoString && worldInfoString.trim()) {
systemMessage += worldInfoString.trim(); systemMessage += worldInfoString.trim();
worldInfoAdded = true; worldInfoAdded = true;
console.log('[RPG Companion] ✅ Added world info from getWorldInfoPrompt'); // console.log('[RPG Companion] ✅ Added world info from getWorldInfoPrompt');
} }
} else { } else {
console.log('[RPG Companion] getWorldInfoPrompt not available or no chat'); // console.log('[RPG Companion] getWorldInfoPrompt not available or no chat');
} }
} catch (e) { } catch (e) {
console.warn('[RPG Companion] Failed to get world info from getWorldInfoPrompt:', e); console.warn('[RPG Companion] Failed to get world info from getWorldInfoPrompt:', e);
@@ -135,7 +135,7 @@ export async function buildEncounterInitPrompt() {
// Fallback to activatedWorldInfo // Fallback to activatedWorldInfo
if (!worldInfoAdded && context.activatedWorldInfo && Array.isArray(context.activatedWorldInfo) && context.activatedWorldInfo.length > 0) { if (!worldInfoAdded && context.activatedWorldInfo && Array.isArray(context.activatedWorldInfo) && context.activatedWorldInfo.length > 0) {
console.log('[RPG Companion] Using fallback activatedWorldInfo:', context.activatedWorldInfo.length, 'entries'); // console.log('[RPG Companion] Using fallback activatedWorldInfo:', context.activatedWorldInfo.length, 'entries');
context.activatedWorldInfo.forEach((entry) => { context.activatedWorldInfo.forEach((entry) => {
if (entry && entry.content) { if (entry && entry.content) {
systemMessage += `${entry.content}\n\n`; systemMessage += `${entry.content}\n\n`;
@@ -745,7 +745,7 @@ export function parseEncounterJSON(response) {
const repaired = repairJSON(cleaned); const repaired = repairJSON(cleaned);
if (repaired) { if (repaired) {
console.log('[RPG Companion] ✓ Successfully repaired encounter JSON'); // console.log('[RPG Companion] ✓ Successfully repaired encounter JSON');
return repaired; return repaired;
} }
+53 -53
View File
@@ -44,15 +44,15 @@ let lastCommittedChatLength = -1;
export async function onGenerationStarted(type, data, dryRun) { export async function onGenerationStarted(type, data, dryRun) {
// Skip dry runs (page reload, prompt manager preview, etc.) // Skip dry runs (page reload, prompt manager preview, etc.)
if (dryRun) { if (dryRun) {
console.log('[RPG Companion] Skipping onGenerationStarted: dry run detected'); // console.log('[RPG Companion] Skipping onGenerationStarted: dry run detected');
return; return;
} }
console.log('[RPG Companion] onGenerationStarted called'); // console.log('[RPG Companion] onGenerationStarted called');
console.log('[RPG Companion] enabled:', extensionSettings.enabled); // console.log('[RPG Companion] enabled:', extensionSettings.enabled);
console.log('[RPG Companion] generationMode:', extensionSettings.generationMode); // console.log('[RPG Companion] generationMode:', extensionSettings.generationMode);
console.log('[RPG Companion] ⚡ EVENT: onGenerationStarted - lastActionWasSwipe =', lastActionWasSwipe, '| isGenerating =', isGenerating); // console.log('[RPG Companion] ⚡ EVENT: onGenerationStarted - lastActionWasSwipe =', lastActionWasSwipe, '| isGenerating =', isGenerating);
console.log('[RPG Companion] Committed Prompt:', committedTrackerData); // console.log('[RPG Companion] Committed Prompt:', committedTrackerData);
// Skip tracker injection for image generation requests // Skip tracker injection for image generation requests
if (data?.quietImage) { if (data?.quietImage) {
@@ -115,18 +115,18 @@ export async function onGenerationStarted(type, data, dryRun) {
const shouldCommit = isUserMessage && !lastActionWasSwipe && currentChatLength !== lastCommittedChatLength; const shouldCommit = isUserMessage && !lastActionWasSwipe && currentChatLength !== lastCommittedChatLength;
if (shouldCommit) { if (shouldCommit) {
console.log('[RPG Companion] 📝 TOGETHER MODE COMMIT: User sent message - committing data from BEFORE user message'); // console.log('[RPG Companion] 📝 TOGETHER MODE COMMIT: User sent message - committing data from BEFORE user message');
console.log('[RPG Companion] Chat length:', currentChatLength, 'Last committed:', lastCommittedChatLength); // console.log('[RPG Companion] Chat length:', currentChatLength, 'Last committed:', lastCommittedChatLength);
console.log('[RPG Companion] BEFORE: committedTrackerData =', { // console.log('[RPG Companion] BEFORE: committedTrackerData =', {
userStats: committedTrackerData.userStats ? `${committedTrackerData.userStats.substring(0, 50)}...` : 'null', // userStats: committedTrackerData.userStats ? `${committedTrackerData.userStats.substring(0, 50)}...` : 'null',
infoBox: committedTrackerData.infoBox ? 'exists' : 'null', // infoBox: committedTrackerData.infoBox ? 'exists' : 'null',
characterThoughts: committedTrackerData.characterThoughts ? `${committedTrackerData.characterThoughts.substring(0, 100)}...` : 'null' // characterThoughts: committedTrackerData.characterThoughts ? `${committedTrackerData.characterThoughts.substring(0, 100)}...` : 'null'
}); // // });
console.log('[RPG Companion] BEFORE: lastGeneratedData =', { // console.log('[RPG Companion] BEFORE: lastGeneratedData =', {
userStats: lastGeneratedData.userStats ? `${lastGeneratedData.userStats.substring(0, 50)}...` : 'null', // userStats: lastGeneratedData.userStats ? `${lastGeneratedData.userStats.substring(0, 50)}...` : 'null',
infoBox: lastGeneratedData.infoBox ? 'exists' : 'null', // infoBox: lastGeneratedData.infoBox ? 'exists' : 'null',
characterThoughts: lastGeneratedData.characterThoughts ? `${lastGeneratedData.characterThoughts.substring(0, 100)}...` : 'null' // characterThoughts: lastGeneratedData.characterThoughts ? `${lastGeneratedData.characterThoughts.substring(0, 100)}...` : 'null'
}); // });
// Commit displayed data (from before user sent message) // Commit displayed data (from before user sent message)
committedTrackerData.userStats = lastGeneratedData.userStats; committedTrackerData.userStats = lastGeneratedData.userStats;
@@ -136,23 +136,23 @@ export async function onGenerationStarted(type, data, dryRun) {
// Track chat length to prevent duplicate commits // Track chat length to prevent duplicate commits
lastCommittedChatLength = currentChatLength; lastCommittedChatLength = currentChatLength;
console.log('[RPG Companion] AFTER: committedTrackerData =', { // console.log('[RPG Companion] AFTER: committedTrackerData =', {
userStats: committedTrackerData.userStats ? `${committedTrackerData.userStats.substring(0, 50)}...` : 'null', // userStats: committedTrackerData.userStats ? `${committedTrackerData.userStats.substring(0, 50)}...` : 'null',
infoBox: committedTrackerData.infoBox ? 'exists' : 'null', // infoBox: committedTrackerData.infoBox ? 'exists' : 'null',
characterThoughts: committedTrackerData.characterThoughts ? `${committedTrackerData.characterThoughts.substring(0, 100)}...` : 'null' // characterThoughts: committedTrackerData.characterThoughts ? `${committedTrackerData.characterThoughts.substring(0, 100)}...` : 'null'
}); // });
} else if (lastActionWasSwipe) { } else if (lastActionWasSwipe) {
console.log('[RPG Companion] ⏭️ Skipping commit: swipe (using previous committed data)'); // console.log('[RPG Companion] ⏭️ Skipping commit: swipe (using previous committed data)');
} else if (!isUserMessage) { } else if (!isUserMessage) {
console.log('[RPG Companion] ⏭️ Skipping commit: second-to-last message is not user message (likely swipe or continuation)'); // console.log('[RPG Companion] ⏭️ Skipping commit: second-to-last message is not user message (likely swipe or continuation)');
} }
console.log('[RPG Companion] 📦 TOGETHER MODE: Injecting committed tracker data into prompt'); // console.log('[RPG Companion] 📦 TOGETHER MODE: Injecting committed tracker data into prompt');
console.log('[RPG Companion] committedTrackerData =', { // console.log('[RPG Companion] committedTrackerData =', {
userStats: committedTrackerData.userStats ? `${committedTrackerData.userStats.substring(0, 50)}...` : 'null', // userStats: committedTrackerData.userStats ? `${committedTrackerData.userStats.substring(0, 50)}...` : 'null',
infoBox: committedTrackerData.infoBox ? 'exists' : 'null', // infoBox: committedTrackerData.infoBox ? 'exists' : 'null',
characterThoughts: committedTrackerData.characterThoughts ? `${committedTrackerData.characterThoughts.substring(0, 100)}...` : 'null' // characterThoughts: committedTrackerData.characterThoughts ? `${committedTrackerData.characterThoughts.substring(0, 100)}...` : 'null'
}); // });
} }
// For SEPARATE mode only: Check if we need to commit extension data // For SEPARATE mode only: Check if we need to commit extension data
@@ -162,35 +162,35 @@ export async function onGenerationStarted(type, data, dryRun) {
if (extensionSettings.generationMode === 'separate' && !isGenerating) { if (extensionSettings.generationMode === 'separate' && !isGenerating) {
if (!lastActionWasSwipe) { if (!lastActionWasSwipe) {
// User sent a new message - commit lastGeneratedData before generation // User sent a new message - commit lastGeneratedData before generation
console.log('[RPG Companion] 📝 COMMIT: New message - committing lastGeneratedData'); // console.log('[RPG Companion] 📝 COMMIT: New message - committing lastGeneratedData');
console.log('[RPG Companion] BEFORE commit - committedTrackerData:', { // console.log('[RPG Companion] BEFORE commit - committedTrackerData:', {
userStats: committedTrackerData.userStats ? 'exists' : 'null', // userStats: committedTrackerData.userStats ? 'exists' : 'null',
infoBox: committedTrackerData.infoBox ? 'exists' : 'null', // infoBox: committedTrackerData.infoBox ? 'exists' : 'null',
characterThoughts: committedTrackerData.characterThoughts ? 'exists' : 'null' // characterThoughts: committedTrackerData.characterThoughts ? 'exists' : 'null'
}); // // });
console.log('[RPG Companion] BEFORE commit - lastGeneratedData:', { // console.log('[RPG Companion] BEFORE commit - lastGeneratedData:', {
userStats: lastGeneratedData.userStats ? 'exists' : 'null', // userStats: lastGeneratedData.userStats ? 'exists' : 'null',
infoBox: lastGeneratedData.infoBox ? 'exists' : 'null', // infoBox: lastGeneratedData.infoBox ? 'exists' : 'null',
characterThoughts: lastGeneratedData.characterThoughts ? 'exists' : 'null' // characterThoughts: lastGeneratedData.characterThoughts ? 'exists' : 'null'
}); // });
committedTrackerData.userStats = lastGeneratedData.userStats; committedTrackerData.userStats = lastGeneratedData.userStats;
committedTrackerData.infoBox = lastGeneratedData.infoBox; committedTrackerData.infoBox = lastGeneratedData.infoBox;
committedTrackerData.characterThoughts = lastGeneratedData.characterThoughts; committedTrackerData.characterThoughts = lastGeneratedData.characterThoughts;
console.log('[RPG Companion] AFTER commit - committedTrackerData:', { // console.log('[RPG Companion] AFTER commit - committedTrackerData:', {
userStats: committedTrackerData.userStats ? 'exists' : 'null', // userStats: committedTrackerData.userStats ? 'exists' : 'null',
infoBox: committedTrackerData.infoBox ? 'exists' : 'null', // infoBox: committedTrackerData.infoBox ? 'exists' : 'null',
characterThoughts: committedTrackerData.characterThoughts ? 'exists' : 'null' // characterThoughts: committedTrackerData.characterThoughts ? 'exists' : 'null'
}); // });
// Reset flag after committing (ready for next cycle) // Reset flag after committing (ready for next cycle)
} else { } else {
console.log('[RPG Companion] 🔄 SWIPE: Using existing committedTrackerData (no commit)'); // console.log('[RPG Companion] 🔄 SWIPE: Using existing committedTrackerData (no commit)');
console.log('[RPG Companion] committedTrackerData:', { // console.log('[RPG Companion] committedTrackerData:', {
userStats: committedTrackerData.userStats ? 'exists' : 'null', // userStats: committedTrackerData.userStats ? 'exists' : 'null',
infoBox: committedTrackerData.infoBox ? 'exists' : 'null', // infoBox: committedTrackerData.infoBox ? 'exists' : 'null',
characterThoughts: committedTrackerData.characterThoughts ? 'exists' : 'null' // characterThoughts: committedTrackerData.characterThoughts ? 'exists' : 'null'
}); // });
// Reset flag after using it (swipe generation complete, ready for next action) // Reset flag after using it (swipe generation complete, ready for next action)
} }
} }
+16 -16
View File
@@ -214,9 +214,9 @@ function applyInfoBoxLocks(data, lockedItems) {
* @returns {string} JSON string with locks applied * @returns {string} JSON string with locks applied
*/ */
function applyCharactersLocks(data, lockedItems) { function applyCharactersLocks(data, lockedItems) {
console.log('[Lock Manager] applyCharactersLocks called'); // console.log('[Lock Manager] applyCharactersLocks called');
console.log('[Lock Manager] Locked items:', JSON.stringify(lockedItems, null, 2)); // console.log('[Lock Manager] Locked items:', JSON.stringify(lockedItems, null, 2));
console.log('[Lock Manager] Input data:', JSON.stringify(data, null, 2)); // console.log('[Lock Manager] Input data:', JSON.stringify(data, null, 2));
// Handle both array format and object format // Handle both array format and object format
let characters = Array.isArray(data) ? data : (data.characters || []); let characters = Array.isArray(data) ? data : (data.characters || []);
@@ -226,7 +226,7 @@ function applyCharactersLocks(data, lockedItems) {
// Check if entire character is locked (index-based) // Check if entire character is locked (index-based)
if (lockedItems[index] === true) { if (lockedItems[index] === true) {
console.log('[Lock Manager] Locking entire character by index:', index); // console.log('[Lock Manager] Locking entire character by index:', index);
return { ...char, locked: true }; return { ...char, locked: true };
} }
@@ -235,7 +235,7 @@ function applyCharactersLocks(data, lockedItems) {
if (charLocks === true) { if (charLocks === true) {
// Entire character is locked // Entire character is locked
console.log('[Lock Manager] Locking entire character:', charName); // console.log('[Lock Manager] Locking entire character:', charName);
return { ...char, locked: true }; return { ...char, locked: true };
} else if (charLocks && typeof charLocks === 'object') { } else if (charLocks && typeof charLocks === 'object') {
// Character has field-level locks // Character has field-level locks
@@ -255,14 +255,14 @@ function applyCharactersLocks(data, lockedItems) {
// Check at root level first (backward compatibility) // Check at root level first (backward compatibility)
if (modifiedChar[fieldName] !== undefined) { if (modifiedChar[fieldName] !== undefined) {
console.log('[Lock Manager] Applying lock to field:', `${charName}.${fieldName}`); // console.log('[Lock Manager] Applying lock to field:', `${charName}.${fieldName}`);
modifiedChar[fieldName] = { modifiedChar[fieldName] = {
value: modifiedChar[fieldName], value: modifiedChar[fieldName],
locked: true locked: true
}; };
locked = true; locked = true;
} else if (modifiedChar[snakeCaseFieldName] !== undefined) { } else if (modifiedChar[snakeCaseFieldName] !== undefined) {
console.log('[Lock Manager] Applying lock to snake_case field:', `${charName}.${snakeCaseFieldName} (from ${fieldName})`); // console.log('[Lock Manager] Applying lock to snake_case field:', `${charName}.${snakeCaseFieldName} (from ${fieldName})`);
modifiedChar[snakeCaseFieldName] = { modifiedChar[snakeCaseFieldName] = {
value: modifiedChar[snakeCaseFieldName], value: modifiedChar[snakeCaseFieldName],
locked: true locked: true
@@ -273,7 +273,7 @@ function applyCharactersLocks(data, lockedItems) {
// Check in nested objects (details, relationship, thoughts) // Check in nested objects (details, relationship, thoughts)
if (!locked && modifiedChar.details) { if (!locked && modifiedChar.details) {
if (modifiedChar.details[fieldName] !== undefined) { if (modifiedChar.details[fieldName] !== undefined) {
console.log('[Lock Manager] Applying lock to details field:', `${charName}.details.${fieldName}`); // console.log('[Lock Manager] Applying lock to details field:', `${charName}.details.${fieldName}`);
if (!modifiedChar.details || typeof modifiedChar.details !== 'object') { if (!modifiedChar.details || typeof modifiedChar.details !== 'object') {
modifiedChar.details = {}; modifiedChar.details = {};
} else { } else {
@@ -285,7 +285,7 @@ function applyCharactersLocks(data, lockedItems) {
}; };
locked = true; locked = true;
} else if (modifiedChar.details[snakeCaseFieldName] !== undefined) { } else if (modifiedChar.details[snakeCaseFieldName] !== undefined) {
console.log('[Lock Manager] Applying lock to details snake_case field:', `${charName}.details.${snakeCaseFieldName} (from ${fieldName})`); // console.log('[Lock Manager] Applying lock to details snake_case field:', `${charName}.details.${snakeCaseFieldName} (from ${fieldName})`);
if (!modifiedChar.details || typeof modifiedChar.details !== 'object') { if (!modifiedChar.details || typeof modifiedChar.details !== 'object') {
modifiedChar.details = {}; modifiedChar.details = {};
} else { } else {
@@ -302,7 +302,7 @@ function applyCharactersLocks(data, lockedItems) {
// Check in relationship object // Check in relationship object
if (!locked && modifiedChar.relationship) { if (!locked && modifiedChar.relationship) {
if (modifiedChar.relationship[fieldName] !== undefined) { if (modifiedChar.relationship[fieldName] !== undefined) {
console.log('[Lock Manager] Applying lock to relationship field:', `${charName}.relationship.${fieldName}`); // console.log('[Lock Manager] Applying lock to relationship field:', `${charName}.relationship.${fieldName}`);
modifiedChar.relationship = { ...modifiedChar.relationship }; modifiedChar.relationship = { ...modifiedChar.relationship };
modifiedChar.relationship[fieldName] = { modifiedChar.relationship[fieldName] = {
value: modifiedChar.relationship[fieldName], value: modifiedChar.relationship[fieldName],
@@ -310,7 +310,7 @@ function applyCharactersLocks(data, lockedItems) {
}; };
locked = true; locked = true;
} else if (modifiedChar.relationship[snakeCaseFieldName] !== undefined) { } else if (modifiedChar.relationship[snakeCaseFieldName] !== undefined) {
console.log('[Lock Manager] Applying lock to relationship snake_case field:', `${charName}.relationship.${snakeCaseFieldName} (from ${fieldName})`); // console.log('[Lock Manager] Applying lock to relationship snake_case field:', `${charName}.relationship.${snakeCaseFieldName} (from ${fieldName})`);
modifiedChar.relationship = { ...modifiedChar.relationship }; modifiedChar.relationship = { ...modifiedChar.relationship };
modifiedChar.relationship[snakeCaseFieldName] = { modifiedChar.relationship[snakeCaseFieldName] = {
value: modifiedChar.relationship[snakeCaseFieldName], value: modifiedChar.relationship[snakeCaseFieldName],
@@ -323,7 +323,7 @@ function applyCharactersLocks(data, lockedItems) {
// Check in thoughts object // Check in thoughts object
if (!locked && modifiedChar.thoughts) { if (!locked && modifiedChar.thoughts) {
if (modifiedChar.thoughts[fieldName] !== undefined) { if (modifiedChar.thoughts[fieldName] !== undefined) {
console.log('[Lock Manager] Applying lock to thoughts field:', `${charName}.thoughts.${fieldName}`); // console.log('[Lock Manager] Applying lock to thoughts field:', `${charName}.thoughts.${fieldName}`);
modifiedChar.thoughts = { ...modifiedChar.thoughts }; modifiedChar.thoughts = { ...modifiedChar.thoughts };
modifiedChar.thoughts[fieldName] = { modifiedChar.thoughts[fieldName] = {
value: modifiedChar.thoughts[fieldName], value: modifiedChar.thoughts[fieldName],
@@ -331,7 +331,7 @@ function applyCharactersLocks(data, lockedItems) {
}; };
locked = true; locked = true;
} else if (modifiedChar.thoughts[snakeCaseFieldName] !== undefined) { } else if (modifiedChar.thoughts[snakeCaseFieldName] !== undefined) {
console.log('[Lock Manager] Applying lock to thoughts snake_case field:', `${charName}.thoughts.${snakeCaseFieldName} (from ${fieldName})`); // console.log('[Lock Manager] Applying lock to thoughts snake_case field:', `${charName}.thoughts.${snakeCaseFieldName} (from ${fieldName})`);
modifiedChar.thoughts = { ...modifiedChar.thoughts }; modifiedChar.thoughts = { ...modifiedChar.thoughts };
modifiedChar.thoughts[snakeCaseFieldName] = { modifiedChar.thoughts[snakeCaseFieldName] = {
value: modifiedChar.thoughts[snakeCaseFieldName], value: modifiedChar.thoughts[snakeCaseFieldName],
@@ -354,7 +354,7 @@ function applyCharactersLocks(data, lockedItems) {
? JSON.stringify(characters, null, 2) ? JSON.stringify(characters, null, 2)
: JSON.stringify({ ...data, characters }, null, 2); : JSON.stringify({ ...data, characters }, null, 2);
console.log('[Lock Manager] Output data:', result); // console.log('[Lock Manager] Output data:', result);
return result; return result;
} }
@@ -429,7 +429,7 @@ export function isItemLocked(trackerType, itemPath) {
* @param {boolean} locked - New lock state * @param {boolean} locked - New lock state
*/ */
export function setItemLock(trackerType, itemPath, locked) { export function setItemLock(trackerType, itemPath, locked) {
console.log('[Lock Manager] setItemLock called:', { trackerType, itemPath, locked }); // console.log('[Lock Manager] setItemLock called:', { trackerType, itemPath, locked });
if (!extensionSettings.lockedItems) { if (!extensionSettings.lockedItems) {
extensionSettings.lockedItems = {}; extensionSettings.lockedItems = {};
@@ -459,5 +459,5 @@ export function setItemLock(trackerType, itemPath, locked) {
delete current[finalKey]; delete current[finalKey];
} }
console.log('[Lock Manager] Locked items after set:', JSON.stringify(extensionSettings.lockedItems, null, 2)); // console.log('[Lock Manager] Locked items after set:', JSON.stringify(extensionSettings.lockedItems, null, 2));
} }
+44 -38
View File
@@ -129,7 +129,7 @@ function stripBrackets(text) {
* Helper to log to both console and debug logs array * Helper to log to both console and debug logs array
*/ */
function debugLog(message, data = null) { function debugLog(message, data = null) {
console.log(message, data || ''); // console.log(message, data || '');
if (extensionSettings.debugMode) { if (extensionSettings.debugMode) {
addDebugLog(message, data); addDebugLog(message, data);
} }
@@ -209,30 +209,30 @@ export function parseResponse(responseText) {
} }
if (extractedObjects.length > 0) { if (extractedObjects.length > 0) {
console.log(`[RPG Parser] ✓ Found ${extractedObjects.length} raw JSON objects (v3 format)`); // console.log(`[RPG Parser] ✓ Found ${extractedObjects.length} raw JSON objects (v3 format)`);
debugLog(`[RPG Parser] ✓ Found ${extractedObjects.length} raw JSON objects (v3 format)`); debugLog(`[RPG Parser] ✓ Found ${extractedObjects.length} raw JSON objects (v3 format)`);
// First, try to parse as unified JSON structure (new v3.1 format) // First, try to parse as unified JSON structure (new v3.1 format)
if (extractedObjects.length === 1) { if (extractedObjects.length === 1) {
const parsed = repairJSON(extractedObjects[0]); const parsed = repairJSON(extractedObjects[0]);
if (parsed && (parsed.userStats || parsed.infoBox || parsed.characters)) { if (parsed && (parsed.userStats || parsed.infoBox || parsed.characters)) {
console.log('[RPG Parser] ✓ Detected unified JSON structure (v3.1 format)'); // console.log('[RPG Parser] ✓ Detected unified JSON structure (v3.1 format)');
if (parsed.userStats) { if (parsed.userStats) {
result.userStats = JSON.stringify(parsed.userStats); result.userStats = JSON.stringify(parsed.userStats);
console.log('[RPG Parser] ✓ Extracted userStats from unified structure'); // console.log('[RPG Parser] ✓ Extracted userStats from unified structure');
} }
if (parsed.infoBox) { if (parsed.infoBox) {
result.infoBox = JSON.stringify(parsed.infoBox); result.infoBox = JSON.stringify(parsed.infoBox);
console.log('[RPG Parser] ✓ Extracted infoBox from unified structure'); // console.log('[RPG Parser] ✓ Extracted infoBox from unified structure');
} }
if (parsed.characters) { if (parsed.characters) {
result.characterThoughts = JSON.stringify(parsed.characters); result.characterThoughts = JSON.stringify(parsed.characters);
console.log('[RPG Parser] ✓ Extracted characters from unified structure'); // console.log('[RPG Parser] ✓ Extracted characters from unified structure');
} }
if (result.userStats || result.infoBox || result.characterThoughts) { if (result.userStats || result.infoBox || result.characterThoughts) {
console.log('[RPG Parser] ✓ Returning unified JSON parse results'); // console.log('[RPG Parser] ✓ Returning unified JSON parse results');
debugLog('[RPG Parser] Returning unified JSON parse results'); debugLog('[RPG Parser] Returning unified JSON parse results');
return result; return result;
} }
@@ -242,13 +242,13 @@ export function parseResponse(responseText) {
// Fall back to parsing multiple separate JSON objects (legacy v3.0 format) // Fall back to parsing multiple separate JSON objects (legacy v3.0 format)
for (let idx = 0; idx < extractedObjects.length; idx++) { for (let idx = 0; idx < extractedObjects.length; idx++) {
const jsonContent = extractedObjects[idx]; const jsonContent = extractedObjects[idx];
console.log(`[RPG Parser] Parsing object ${idx + 1}:`, jsonContent.substring(0, 100) + '...'); // console.log(`[RPG Parser] Parsing object ${idx + 1}:`, jsonContent.substring(0, 100) + '...');
console.log(`[RPG Parser] Full object ${idx + 1} length:`, jsonContent.length); // console.log(`[RPG Parser] Full object ${idx + 1} length:`, jsonContent.length);
const parsed = repairJSON(jsonContent); const parsed = repairJSON(jsonContent);
if (parsed) { if (parsed) {
console.log(`[RPG Parser] Object ${idx + 1} parsed successfully, keys:`, Object.keys(parsed)); // console.log(`[RPG Parser] Object ${idx + 1} parsed successfully, keys:`, Object.keys(parsed));
// Check if object is wrapped (e.g., {"userStats": {...}}) // Check if object is wrapped (e.g., {"userStats": {...}})
// Unwrap single-key objects that match our tracker types // Unwrap single-key objects that match our tracker types
@@ -257,22 +257,22 @@ export function parseResponse(responseText) {
const key = Object.keys(parsed)[0]; const key = Object.keys(parsed)[0];
if (key === 'userStats' || key === 'infoBox' || key === 'characters') { if (key === 'userStats' || key === 'infoBox' || key === 'characters') {
unwrapped = parsed[key]; unwrapped = parsed[key];
console.log(`[RPG Parser] ✓ Unwrapped ${key} object`); // console.log(`[RPG Parser] ✓ Unwrapped ${key} object`);
} }
} }
// Detect tracker type by checking for top-level fields // Detect tracker type by checking for top-level fields
if (unwrapped.stats || unwrapped.status || unwrapped.skills || unwrapped.inventory || unwrapped.quests) { if (unwrapped.stats || unwrapped.status || unwrapped.skills || unwrapped.inventory || unwrapped.quests) {
result.userStats = jsonContent; result.userStats = jsonContent;
console.log('[RPG Parser] ✓ Assigned to User Stats'); // console.log('[RPG Parser] ✓ Assigned to User Stats');
debugLog('[RPG Parser] ✓ Extracted raw JSON User Stats'); debugLog('[RPG Parser] ✓ Extracted raw JSON User Stats');
} else if (unwrapped.date || unwrapped.location || unwrapped.weather || unwrapped.temperature || unwrapped.time) { } else if (unwrapped.date || unwrapped.location || unwrapped.weather || unwrapped.temperature || unwrapped.time) {
result.infoBox = jsonContent; result.infoBox = jsonContent;
console.log('[RPG Parser] ✓ Assigned to Info Box'); // console.log('[RPG Parser] ✓ Assigned to Info Box');
debugLog('[RPG Parser] ✓ Extracted raw JSON Info Box'); debugLog('[RPG Parser] ✓ Extracted raw JSON Info Box');
} else if (unwrapped.characters || Array.isArray(unwrapped)) { } else if (unwrapped.characters || Array.isArray(unwrapped)) {
result.characterThoughts = jsonContent; result.characterThoughts = jsonContent;
console.log('[RPG Parser] ✓ Assigned to Characters'); // console.log('[RPG Parser] ✓ Assigned to Characters');
debugLog('[RPG Parser] ✓ Extracted raw JSON Characters'); debugLog('[RPG Parser] ✓ Extracted raw JSON Characters');
} else { } else {
console.warn('[RPG Parser] ⚠️ Could not categorize object with keys:', Object.keys(parsed)); console.warn('[RPG Parser] ⚠️ Could not categorize object with keys:', Object.keys(parsed));
@@ -283,11 +283,11 @@ export function parseResponse(responseText) {
} }
if (result.userStats || result.infoBox || result.characterThoughts) { if (result.userStats || result.infoBox || result.characterThoughts) {
console.log('[RPG Parser] ✓ Returning raw JSON parse results:', { // console.log('[RPG Parser] ✓ Returning raw JSON parse results:', {
hasUserStats: !!result.userStats, // hasUserStats: !!result.userStats,
hasInfoBox: !!result.infoBox, // hasInfoBox: !!result.infoBox,
hasCharacters: !!result.characterThoughts // hasCharacters: !!result.characterThoughts
}); // });
debugLog('[RPG Parser] Returning raw JSON parse results'); debugLog('[RPG Parser] Returning raw JSON parse results');
return result; return result;
} else { } else {
@@ -301,31 +301,31 @@ export function parseResponse(responseText) {
const jsonMatches = [...cleanedResponse.matchAll(jsonBlockRegex)]; const jsonMatches = [...cleanedResponse.matchAll(jsonBlockRegex)];
if (jsonMatches.length > 0) { if (jsonMatches.length > 0) {
console.log('[RPG Parser] ✓ Found', jsonMatches.length, 'JSON code blocks (v3 format with fences)'); // console.log('[RPG Parser] ✓ Found', jsonMatches.length, 'JSON code blocks (v3 format with fences)');
debugLog('[RPG Parser] ✓ Found JSON code blocks (v3 format), parsing as JSON'); debugLog('[RPG Parser] ✓ Found JSON code blocks (v3 format), parsing as JSON');
for (let idx = 0; idx < jsonMatches.length; idx++) { for (let idx = 0; idx < jsonMatches.length; idx++) {
const match = jsonMatches[idx]; const match = jsonMatches[idx];
const jsonContent = match[1].trim(); const jsonContent = match[1].trim();
console.log(`[RPG Parser] Parsing JSON block ${idx + 1}:`, jsonContent.substring(0, 100) + '...'); // console.log(`[RPG Parser] Parsing JSON block ${idx + 1}:`, jsonContent.substring(0, 100) + '...');
const parsed = repairJSON(jsonContent); const parsed = repairJSON(jsonContent);
if (parsed) { if (parsed) {
console.log(`[RPG Parser] JSON block ${idx + 1} parsed successfully, keys:`, Object.keys(parsed)); // console.log(`[RPG Parser] JSON block ${idx + 1} parsed successfully, keys:`, Object.keys(parsed));
// Detect tracker type by checking for top-level fields // Detect tracker type by checking for top-level fields
if (parsed.stats || parsed.status || parsed.skills || parsed.inventory || parsed.quests) { if (parsed.stats || parsed.status || parsed.skills || parsed.inventory || parsed.quests) {
result.userStats = jsonContent; result.userStats = jsonContent;
console.log('[RPG Parser] ✓ Assigned to User Stats'); // console.log('[RPG Parser] ✓ Assigned to User Stats');
debugLog('[RPG Parser] ✓ Extracted JSON User Stats'); debugLog('[RPG Parser] ✓ Extracted JSON User Stats');
} else if (parsed.date || parsed.location || parsed.weather || parsed.temperature || parsed.time) { } else if (parsed.date || parsed.location || parsed.weather || parsed.temperature || parsed.time) {
result.infoBox = jsonContent; result.infoBox = jsonContent;
console.log('[RPG Parser] ✓ Assigned to Info Box'); // console.log('[RPG Parser] ✓ Assigned to Info Box');
debugLog('[RPG Parser] ✓ Extracted JSON Info Box'); debugLog('[RPG Parser] ✓ Extracted JSON Info Box');
} else if (parsed.characters || Array.isArray(parsed)) { } else if (parsed.characters || Array.isArray(parsed)) {
result.characterThoughts = jsonContent; result.characterThoughts = jsonContent;
console.log('[RPG Parser] ✓ Assigned to Characters'); // console.log('[RPG Parser] ✓ Assigned to Characters');
debugLog('[RPG Parser] ✓ Extracted JSON Characters'); debugLog('[RPG Parser] ✓ Extracted JSON Characters');
} else { } else {
console.warn('[RPG Parser] ⚠️ Could not categorize JSON block with keys:', Object.keys(parsed)); console.warn('[RPG Parser] ⚠️ Could not categorize JSON block with keys:', Object.keys(parsed));
@@ -339,11 +339,11 @@ export function parseResponse(responseText) {
// If we found at least one valid JSON block, return the result // If we found at least one valid JSON block, return the result
// Mixed formats (some JSON, some text) will still work // Mixed formats (some JSON, some text) will still work
if (result.userStats || result.infoBox || result.characterThoughts) { if (result.userStats || result.infoBox || result.characterThoughts) {
console.log('[RPG Parser] ✓ Returning JSON code block parse results:', { // console.log('[RPG Parser] ✓ Returning JSON code block parse results:', {
hasUserStats: !!result.userStats, // hasUserStats: !!result.userStats,
hasInfoBox: !!result.infoBox, // hasInfoBox: !!result.infoBox,
hasCharacters: !!result.characterThoughts // hasCharacters: !!result.characterThoughts
}); // });
debugLog('[RPG Parser] Returning JSON parse results'); debugLog('[RPG Parser] Returning JSON parse results');
return result; return result;
} else { } else {
@@ -500,6 +500,12 @@ export function parseResponse(responseText) {
debugLog('[RPG Parser] Found Characters:', !!result.characterThoughts); debugLog('[RPG Parser] Found Characters:', !!result.characterThoughts);
debugLog('[RPG Parser] ======================================================='); debugLog('[RPG Parser] =======================================================');
// Check if we found at least one section - if not, mark as parsing failure
if (!result.userStats && !result.infoBox && !result.characterThoughts) {
result.parsingFailed = true;
console.error('[RPG Parser] ❌ No tracker data found in response - parsing failed');
}
return result; return result;
} // End parseResponse } // End parseResponse
@@ -525,25 +531,25 @@ export function parseUserStats(statsText) {
// Extract stats from v3 JSON structure // Extract stats from v3 JSON structure
if (statsData.stats && Array.isArray(statsData.stats)) { if (statsData.stats && Array.isArray(statsData.stats)) {
console.log('[RPG Parser] ✓ Extracting stats array, count:', statsData.stats.length); // console.log('[RPG Parser] ✓ Extracting stats array, count:', statsData.stats.length);
statsData.stats.forEach(stat => { statsData.stats.forEach(stat => {
if (stat.id && typeof stat.value !== 'undefined') { if (stat.id && typeof stat.value !== 'undefined') {
extensionSettings.userStats[stat.id] = stat.value; extensionSettings.userStats[stat.id] = stat.value;
console.log(`[RPG Parser] ✓ Set ${stat.id} = ${stat.value}`); // console.log(`[RPG Parser] ✓ Set ${stat.id} = ${stat.value}`);
} }
}); });
} }
// Extract status // Extract status
if (statsData.status) { if (statsData.status) {
console.log('[RPG Parser] ✓ Extracting status:', statsData.status); // console.log('[RPG Parser] ✓ Extracting status:', statsData.status);
if (statsData.status.mood) { if (statsData.status.mood) {
extensionSettings.userStats.mood = statsData.status.mood; extensionSettings.userStats.mood = statsData.status.mood;
console.log('[RPG Parser] ✓ Set mood =', statsData.status.mood); // console.log('[RPG Parser] ✓ Set mood =', statsData.status.mood);
} }
if (statsData.status.conditions) { if (statsData.status.conditions) {
extensionSettings.userStats.conditions = statsData.status.conditions; extensionSettings.userStats.conditions = statsData.status.conditions;
console.log('[RPG Parser] ✓ Set conditions =', statsData.status.conditions); // console.log('[RPG Parser] ✓ Set conditions =', statsData.status.conditions);
} }
} }
@@ -587,7 +593,7 @@ export function parseUserStats(statsText) {
stored: convertStoredInventory(inv.stored), stored: convertStoredInventory(inv.stored),
assets: convertItems(inv.assets) assets: convertItems(inv.assets)
}; };
console.log('[RPG Parser] ✓ Converted v3 inventory:', extensionSettings.userStats.inventory); // console.log('[RPG Parser] ✓ Converted v3 inventory:', extensionSettings.userStats.inventory);
} }
// Extract quests (convert v3 object format to v2 string format) // Extract quests (convert v3 object format to v2 string format)
@@ -609,13 +615,13 @@ export function parseUserStats(statsText) {
? statsData.quests.optional.map(convertQuest) ? statsData.quests.optional.map(convertQuest)
: [] : []
}; };
console.log('[RPG Parser] ✓ Converted v3 quests:', extensionSettings.quests); // console.log('[RPG Parser] ✓ Converted v3 quests:', extensionSettings.quests);
} }
// Extract skills if present (store as object, not JSON string) // Extract skills if present (store as object, not JSON string)
if (statsData.skills && Array.isArray(statsData.skills)) { if (statsData.skills && Array.isArray(statsData.skills)) {
extensionSettings.userStats.skills = statsData.skills; extensionSettings.userStats.skills = statsData.skills;
console.log('[RPG Parser] ✓ Set skills:', extensionSettings.userStats.skills); // console.log('[RPG Parser] ✓ Set skills:', extensionSettings.userStats.skills);
} }
debugLog('[RPG Parser] ✓ Successfully extracted v3 JSON data'); debugLog('[RPG Parser] ✓ Successfully extracted v3 JSON data');
+13 -13
View File
@@ -85,7 +85,7 @@ async function getCharacterCardsInfo() {
// Filter out disabled (muted) members // Filter out disabled (muted) members
const disabledMembers = group?.disabled_members || []; const disabledMembers = group?.disabled_members || [];
console.log('[RPG Companion] 🔍 Group ID:', selected_group, '| Disabled members:', disabledMembers); // console.log('[RPG Companion] 🔍 Group ID:', selected_group, '| Disabled members:', disabledMembers);
let characterIndex = 0; let characterIndex = 0;
groupMembers.forEach((member) => { groupMembers.forEach((member) => {
@@ -93,7 +93,7 @@ async function getCharacterCardsInfo() {
// Skip muted characters - check against avatar filename // Skip muted characters - check against avatar filename
if (member.avatar && disabledMembers.includes(member.avatar)) { if (member.avatar && disabledMembers.includes(member.avatar)) {
console.log(`[RPG Companion] ❌ Skipping muted: ${member.name} (${member.avatar})`); // console.log(`[RPG Companion] ❌ Skipping muted: ${member.name} (${member.avatar})`);
return; return;
} }
@@ -236,16 +236,16 @@ export function generateTrackerExample() {
// Build unified JSON structure with proper wrapper keys // Build unified JSON structure with proper wrapper keys
const parts = []; const parts = [];
console.log('[RPG Companion] generateTrackerExample - enabled modules:', { // console.log('[RPG Companion] generateTrackerExample - enabled modules:', {
showUserStats: extensionSettings.showUserStats, // showUserStats: extensionSettings.showUserStats,
showInfoBox: extensionSettings.showInfoBox, // showInfoBox: extensionSettings.showInfoBox,
showCharacterThoughts: extensionSettings.showCharacterThoughts // showCharacterThoughts: extensionSettings.showCharacterThoughts
}); // // });
console.log('[RPG Companion] generateTrackerExample - committed data:', { // console.log('[RPG Companion] generateTrackerExample - committed data:', {
hasUserStats: !!committedTrackerData.userStats, // hasUserStats: !!committedTrackerData.userStats,
hasInfoBox: !!committedTrackerData.infoBox, // hasInfoBox: !!committedTrackerData.infoBox,
hasCharacterThoughts: !!committedTrackerData.characterThoughts // hasCharacterThoughts: !!committedTrackerData.characterThoughts
}); // });
if (extensionSettings.showUserStats && committedTrackerData.userStats) { if (extensionSettings.showUserStats && committedTrackerData.userStats) {
// Try to parse as JSON first, otherwise treat as text // Try to parse as JSON first, otherwise treat as text
@@ -285,7 +285,7 @@ export function generateTrackerExample() {
example = '{\n' + parts.join(',\n') + '\n}'; example = '{\n' + parts.join(',\n') + '\n}';
} }
console.log('[RPG Companion] generateTrackerExample - result length:', example.length, 'parts:', parts.length); // console.log('[RPG Companion] generateTrackerExample - result length:', example.length, 'parts:', parts.length);
return example.trim(); return example.trim();
} }
+24 -18
View File
@@ -21,6 +21,7 @@ import {
$musicPlayerContainer $musicPlayerContainer
} from '../../core/state.js'; } from '../../core/state.js';
import { saveChatData, loadChatData } from '../../core/persistence.js'; import { saveChatData, loadChatData } from '../../core/persistence.js';
import { i18n } from '../../core/i18n.js';
// Generation & Parsing // Generation & Parsing
import { parseResponse, parseUserStats } from '../generation/parser.js'; import { parseResponse, parseUserStats } from '../generation/parser.js';
@@ -88,7 +89,7 @@ export function commitTrackerData() {
export function onMessageSent() { export function onMessageSent() {
if (!extensionSettings.enabled) return; if (!extensionSettings.enabled) return;
console.log('[RPG Companion] 🟢 EVENT: onMessageSent - lastActionWasSwipe =', lastActionWasSwipe); // console.log('[RPG Companion] 🟢 EVENT: onMessageSent - lastActionWasSwipe =', lastActionWasSwipe);
// Check if this is a streaming placeholder message (content = "...") // Check if this is a streaming placeholder message (content = "...")
// When streaming is on, ST sends a "..." placeholder before generation starts // When streaming is on, ST sends a "..." placeholder before generation starts
@@ -97,12 +98,12 @@ export function onMessageSent() {
const lastMessage = chat && chat.length > 0 ? chat[chat.length - 1] : null; const lastMessage = chat && chat.length > 0 ? chat[chat.length - 1] : null;
if (lastMessage && lastMessage.mes === '...') { if (lastMessage && lastMessage.mes === '...') {
console.log('[RPG Companion] 🟢 Ignoring onMessageSent: streaming placeholder message'); // console.log('[RPG Companion] 🟢 Ignoring onMessageSent: streaming placeholder message');
return; return;
} }
console.log('[RPG Companion] 🟢 EVENT: onMessageSent (after placeholder check)'); // console.log('[RPG Companion] 🟢 EVENT: onMessageSent (after placeholder check)');
console.log('[RPG Companion] 🟢 NOTE: lastActionWasSwipe will be reset in onMessageReceived after generation completes'); // console.log('[RPG Companion] 🟢 NOTE: lastActionWasSwipe will be reset in onMessageReceived after generation completes');
// For separate mode with auto-update disabled, commit displayed tracker // For separate mode with auto-update disabled, commit displayed tracker
if (extensionSettings.generationMode === 'separate' && !extensionSettings.autoUpdate) { if (extensionSettings.generationMode === 'separate' && !extensionSettings.autoUpdate) {
@@ -111,7 +112,7 @@ export function onMessageSent() {
committedTrackerData.infoBox = lastGeneratedData.infoBox; committedTrackerData.infoBox = lastGeneratedData.infoBox;
committedTrackerData.characterThoughts = lastGeneratedData.characterThoughts; committedTrackerData.characterThoughts = lastGeneratedData.characterThoughts;
console.log('[RPG Companion] 💾 SEPARATE MODE: Committed displayed tracker (auto-update disabled)'); // console.log('[RPG Companion] 💾 SEPARATE MODE: Committed displayed tracker (auto-update disabled)');
} }
} }
} }
@@ -120,7 +121,7 @@ export function onMessageSent() {
* Event handler for when a message is generated. * Event handler for when a message is generated.
*/ */
export async function onMessageReceived(data) { export async function onMessageReceived(data) {
console.log('[RPG Companion] onMessageReceived called, lastActionWasSwipe:', lastActionWasSwipe); // console.log('[RPG Companion] onMessageReceived called, lastActionWasSwipe:', lastActionWasSwipe);
if (!extensionSettings.enabled) { if (!extensionSettings.enabled) {
return; return;
@@ -129,7 +130,7 @@ export async function onMessageReceived(data) {
// Reset swipe flag after generation completes // Reset swipe flag after generation completes
// This ensures next user message (whether from original or swipe) triggers commit // This ensures next user message (whether from original or swipe) triggers commit
setLastActionWasSwipe(false); setLastActionWasSwipe(false);
console.log('[RPG Companion] 🟢 Reset lastActionWasSwipe = false (generation completed)'); // console.log('[RPG Companion] 🟢 Reset lastActionWasSwipe = false (generation completed)');
if (extensionSettings.generationMode === 'together') { if (extensionSettings.generationMode === 'together') {
// In together mode, parse the response to extract RPG data // In together mode, parse the response to extract RPG data
@@ -139,6 +140,11 @@ export async function onMessageReceived(data) {
const responseText = lastMessage.mes; const responseText = lastMessage.mes;
const parsedData = parseResponse(responseText); const parsedData = parseResponse(responseText);
// Check if parsing completely failed (no tracker data found)
if (parsedData.parsingFailed) {
toastr.error(i18n.getTranslation('errors.parsingError'), '', { timeOut: 5000 });
}
// Remove locks from parsed data (JSON format only, text format is unaffected) // Remove locks from parsed data (JSON format only, text format is unaffected)
if (parsedData.userStats) { if (parsedData.userStats) {
parsedData.userStats = removeLocks(parsedData.userStats); parsedData.userStats = removeLocks(parsedData.userStats);
@@ -154,7 +160,7 @@ export async function onMessageReceived(data) {
parseAndStoreSpotifyUrl(responseText); parseAndStoreSpotifyUrl(responseText);
// Update display data with newly parsed response // Update display data with newly parsed response
console.log('[RPG Companion] 📝 TOGETHER MODE: Updating lastGeneratedData with parsed response'); // console.log('[RPG Companion] 📝 TOGETHER MODE: Updating lastGeneratedData with parsed response');
if (parsedData.userStats) { if (parsedData.userStats) {
lastGeneratedData.userStats = parsedData.userStats; lastGeneratedData.userStats = parsedData.userStats;
parseUserStats(parsedData.userStats); parseUserStats(parsedData.userStats);
@@ -227,8 +233,8 @@ export async function onMessageReceived(data) {
// Save to chat metadata // Save to chat metadata
saveChatData(); saveChatData();
} }
} else if (extensionSettings.generationMode === 'separate') { } else if (extensionSettings.generationMode === 'separate' || extensionSettings.generationMode === 'external') {
// In separate mode, also parse Spotify URLs from the main roleplay response // In separate/external mode, also parse Spotify URLs from the main roleplay response
const lastMessage = chat[chat.length - 1]; const lastMessage = chat[chat.length - 1];
if (lastMessage && !lastMessage.is_user) { if (lastMessage && !lastMessage.is_user) {
const responseText = lastMessage.mes; const responseText = lastMessage.mes;
@@ -243,7 +249,7 @@ export async function onMessageReceived(data) {
} }
} }
// Trigger auto-update if enabled // Trigger auto-update if enabled (for both separate and external modes)
if (extensionSettings.autoUpdate) { if (extensionSettings.autoUpdate) {
setTimeout(async () => { setTimeout(async () => {
await updateRPGData(renderUserStats, renderInfoBox, renderThoughts, renderInventory); await updateRPGData(renderUserStats, renderInfoBox, renderThoughts, renderInventory);
@@ -313,12 +319,12 @@ export function onMessageSwiped(messageIndex) {
return; return;
} }
console.log('[RPG Companion] 🔵 EVENT: onMessageSwiped at index:', messageIndex); // console.log('[RPG Companion] 🔵 EVENT: onMessageSwiped at index:', messageIndex);
// Get the message that was swiped // Get the message that was swiped
const message = chat[messageIndex]; const message = chat[messageIndex];
if (!message || message.is_user) { if (!message || message.is_user) {
console.log('[RPG Companion] 🔵 Ignoring swipe - message is user or undefined'); // console.log('[RPG Companion] 🔵 Ignoring swipe - message is user or undefined');
return; return;
} }
@@ -334,10 +340,10 @@ export function onMessageSwiped(messageIndex) {
if (!isExistingSwipe) { if (!isExistingSwipe) {
// This is a NEW swipe that will trigger generation // This is a NEW swipe that will trigger generation
setLastActionWasSwipe(true); setLastActionWasSwipe(true);
console.log('[RPG Companion] 🔵 NEW swipe detected - Set lastActionWasSwipe = true'); // console.log('[RPG Companion] 🔵 NEW swipe detected - Set lastActionWasSwipe = true');
} else { } else {
// This is navigating to an EXISTING swipe - don't change the flag // This is navigating to an EXISTING swipe - don't change the flag
console.log('[RPG Companion] 🔵 EXISTING swipe navigation - lastActionWasSwipe unchanged =', lastActionWasSwipe); // console.log('[RPG Companion] 🔵 EXISTING swipe navigation - lastActionWasSwipe unchanged =', lastActionWasSwipe);
} }
// console.log('[RPG Companion] Loading data for swipe', currentSwipeId); // console.log('[RPG Companion] Loading data for swipe', currentSwipeId);
@@ -358,9 +364,9 @@ export function onMessageSwiped(messageIndex) {
parseUserStats(swipeData.userStats); parseUserStats(swipeData.userStats);
} }
console.log('[RPG Companion] 🔄 Loaded swipe data into lastGeneratedData for display:', currentSwipeId); // console.log('[RPG Companion] 🔄 Loaded swipe data into lastGeneratedData for display:', currentSwipeId);
} else { } else {
console.log('[RPG Companion] ️ No stored data for swipe:', currentSwipeId); // console.log('[RPG Companion] ️ No stored data for swipe:', currentSwipeId);
} }
// Re-render the panels // Re-render the panels
@@ -428,7 +434,7 @@ export function clearExtensionPrompts() {
* Re-applies checkpoint if SillyTavern unhid messages * Re-applies checkpoint if SillyTavern unhid messages
*/ */
export async function onGenerationEnded() { export async function onGenerationEnded() {
console.log('[RPG Companion] 🏁 onGenerationEnded called'); // console.log('[RPG Companion] 🏁 onGenerationEnded called');
// Note: isGenerating flag is cleared in onMessageReceived after parsing (together mode) // Note: isGenerating flag is cleared in onMessageReceived after parsing (together mode)
// or in apiClient.js after separate generation completes (separate mode) // or in apiClient.js after separate generation completes (separate mode)
+32 -9
View File
@@ -75,23 +75,23 @@ function separateEmojiFromText(str) {
* Includes event listeners for editable fields. * Includes event listeners for editable fields.
*/ */
export function renderInfoBox() { export function renderInfoBox() {
console.log('[RPG InfoBox Render] ==================== RENDERING INFO BOX ===================='); // console.log('[RPG InfoBox Render] ==================== RENDERING INFO BOX ====================');
console.log('[RPG InfoBox Render] showInfoBox setting:', extensionSettings.showInfoBox); // console.log('[RPG InfoBox Render] showInfoBox setting:', extensionSettings.showInfoBox);
console.log('[RPG InfoBox Render] Container exists:', !!$infoBoxContainer); // console.log('[RPG InfoBox Render] Container exists:', !!$infoBoxContainer);
if (!extensionSettings.showInfoBox || !$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; return;
} }
// Use committedTrackerData as fallback if lastGeneratedData is empty (e.g., after page refresh) // Use committedTrackerData as fallback if lastGeneratedData is empty (e.g., after page refresh)
const infoBoxData = lastGeneratedData.infoBox || committedTrackerData.infoBox; const infoBoxData = lastGeneratedData.infoBox || committedTrackerData.infoBox;
console.log('[RPG InfoBox Render] infoBoxData length:', infoBoxData ? infoBoxData.length : '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'); // 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 no data yet, hide the container (e.g., after cache clear)
if (!infoBoxData) { if (!infoBoxData) {
console.log('[RPG InfoBox Render] No data, hiding container'); // console.log('[RPG InfoBox Render] No data, hiding container');
$infoBoxContainer.empty().hide(); $infoBoxContainer.empty().hide();
return; return;
} }
@@ -574,6 +574,19 @@ export function renderInfoBox() {
$infoBoxContainer.html(html); $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 // Add event handlers for editable Info Box fields
$infoBoxContainer.find('.rpg-editable').on('blur', function() { $infoBoxContainer.find('.rpg-editable').on('blur', function() {
const $this = $(this); const $this = $(this);
@@ -591,6 +604,11 @@ export function renderInfoBox() {
} }
} }
// Update location text size dynamically
if (field === 'location') {
updateLocationTextSize($this);
}
// Handle recent events separately // Handle recent events separately
if (field === 'event1' || field === 'event2' || field === 'event3') { if (field === 'event1' || field === 'event2' || field === 'event3') {
updateRecentEvent(field, value); 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 // For date fields, show full value on focus
$infoBoxContainer.find('[data-field="month"], [data-field="weekday"], [data-field="year"]').on('focus', function() { $infoBoxContainer.find('[data-field="month"], [data-field="weekday"], [data-field="year"]').on('focus', function() {
const fullValue = $(this).data('full-value'); const fullValue = $(this).data('full-value');
@@ -707,7 +730,7 @@ export function updateInfoBoxField(field, value) {
committedTrackerData.infoBox = lastGeneratedData.infoBox; committedTrackerData.infoBox = lastGeneratedData.infoBox;
saveChatData(); saveChatData();
renderInfoBox(); 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; return;
} }
} }
@@ -1061,6 +1084,6 @@ function updateRecentEvent(field, value) {
window.RPGCompanion.updateWeatherEffect(); window.RPGCompanion.updateWeatherEffect();
} }
console.log(`[RPG Companion] Updated recent event ${field}:`, value); // console.log(`[RPG Companion] Updated recent event ${field}:`, value);
} }
} }
+8 -8
View File
@@ -100,7 +100,7 @@ export function renderMusicPlayer(container) {
// Find the chat form container and insert widget before (above) it // Find the chat form container and insert widget before (above) it
const $chatForm = $('#send_form'); 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) { if ($chatForm.length === 0) {
console.error('[RPG Companion] Music Player: Could not find #send_form - cannot render widget!'); 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 // 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); $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'); 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) { if ($widget.length > 0) {
console.log('[RPG Companion] Music Player: Widget position:', $widget.offset()); // 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 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 display:', $widget.css('display'));
console.log('[RPG Companion] Music Player: Widget CSS visibility:', $widget.css('visibility')); // console.log('[RPG Companion] Music Player: Widget CSS visibility:', $widget.css('visibility'));
} }
// Bind play button click // Bind play button click
+96 -36
View File
@@ -39,7 +39,7 @@ function getLockIconHtml(tracker, path) {
* Helper to log to both console and debug logs array * Helper to log to both console and debug logs array
*/ */
function debugLog(message, data = null) { function debugLog(message, data = null) {
console.log(message, data || ''); // console.log(message, data || '');
if (extensionSettings.debugMode) { if (extensionSettings.debugMode) {
addDebugLog(message, data); addDebugLog(message, data);
} }
@@ -592,7 +592,7 @@ export function renderThoughts() {
const character = $(this).data('character'); const character = $(this).data('character');
const field = $(this).data('field'); const field = $(this).data('field');
const value = $(this).text().trim(); 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); updateCharacterField(character, field, value);
}); });
@@ -723,7 +723,7 @@ export function updateCharacterField(characterName, field, value) {
else if (isThoughtsField && line.startsWith(thoughtsFieldName + ':')) { else if (isThoughtsField && line.startsWith(thoughtsFieldName + ':')) {
// Update thoughts field // Update thoughts field
lines[i] = `${thoughtsFieldName}: ${value}`; 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)); 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) { if (statsLineExists) {
// Update existing Stats line // Update existing Stats line
@@ -750,7 +750,7 @@ export function updateCharacterField(characterName, field, value) {
if (statParts[j].startsWith(field + ':')) { if (statParts[j].startsWith(field + ':')) {
statParts[j] = `${field}: ${numValue}%`; statParts[j] = `${field}: ${numValue}%`;
statFound = true; statFound = true;
console.log('[RPG Companion] Updated stat part:', statParts[j]); // console.log('[RPG Companion] Updated stat part:', statParts[j]);
break; break;
} }
} }
@@ -758,11 +758,11 @@ export function updateCharacterField(characterName, field, value) {
// If stat wasn't found in existing parts, add it // If stat wasn't found in existing parts, add it
if (!statFound) { if (!statFound) {
statParts.push(`${field}: ${numValue}%`); 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(' | ')}`; lines[statsLineIndex] = `Stats: ${statParts.join(' | ')}`;
console.log('[RPG Companion] Updated stats line:', lines[statsLineIndex]); // console.log('[RPG Companion] Updated stats line:', lines[statsLineIndex]);
} else { } else {
// Create new Stats line with all enabled stats (defaulting to 0% except the one being edited) // Create new Stats line with all enabled stats (defaulting to 0% except the one being edited)
const statsParts = enabledCharStats.map(s => { const statsParts = enabledCharStats.map(s => {
@@ -785,7 +785,7 @@ export function updateCharacterField(characterName, field, value) {
} }
lines.splice(insertIndex, 0, newStatsLine); 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 characterEndIndex++; // Adjust end index since we inserted a line
} }
} }
@@ -831,7 +831,7 @@ export function updateCharacterField(characterName, field, value) {
lastGeneratedData.characterThoughts = lines.join('\n'); lastGeneratedData.characterThoughts = lines.join('\n');
committedTrackerData.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; const chat = getContext().chat;
if (chat && chat.length > 0) { if (chat && chat.length > 0) {
@@ -1075,12 +1075,12 @@ function initThoughtIconDragHandlers() {
if (thoughtIconDragHandlersInitialized) return; if (thoughtIconDragHandlersInitialized) return;
thoughtIconDragHandlersInitialized = true; 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 to attach drag handlers to a specific icon element
function attachDragHandlersToIcon($icon) { 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 // Remove any existing handlers
$icon.off('.thoughtIconDrag'); $icon.off('.thoughtIconDrag');
@@ -1089,20 +1089,20 @@ function attachDragHandlersToIcon($icon) {
$icon.on('click.thoughtIconDrag', function(e) { $icon.on('click.thoughtIconDrag', function(e) {
// Check global flag set immediately after drag completes // Check global flag set immediately after drag completes
if (justFinishedDragging) { if (justFinishedDragging) {
console.log('[Thought Icon] CLICK blocked - just finished dragging'); // console.log('[Thought Icon] CLICK blocked - just finished dragging');
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
return false; return false;
} }
console.log('[Thought Icon] CLICK detected on icon!'); // console.log('[Thought Icon] CLICK detected on icon!');
}); });
// Touch drag support - mobile only // Touch drag support - mobile only
$icon.on('touchstart.thoughtIconDrag', function(e) { $icon.on('touchstart.thoughtIconDrag', function(e) {
if (window.innerWidth > 1000) return; if (window.innerWidth > 1000) return;
console.log('[Thought Icon] touchstart'); // console.log('[Thought Icon] touchstart');
touchMoved = false; touchMoved = false;
dragStartTime = Date.now(); dragStartTime = Date.now();
const touch = e.originalEvent.touches[0]; const touch = e.originalEvent.touches[0];
@@ -1120,7 +1120,7 @@ function attachDragHandlersToIcon($icon) {
if (window.innerWidth > 1000) return; if (window.innerWidth > 1000) return;
if (!touchMoved) { if (!touchMoved) {
console.log('[Thought Icon] touchmove - first movement'); // console.log('[Thought Icon] touchmove - first movement');
} }
touchMoved = true; touchMoved = true;
const touch = e.originalEvent.touches[0]; const touch = e.originalEvent.touches[0];
@@ -1160,7 +1160,7 @@ function attachDragHandlersToIcon($icon) {
}); });
$icon.on('touchend.thoughtIconDrag', function(e) { $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) { if (isDragging) {
const offset = $(this).offset(); const offset = $(this).offset();
@@ -1193,7 +1193,7 @@ function attachDragHandlersToIcon($icon) {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
} else if (!touchMoved) { } 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 $panel = $('#rpg-thought-panel');
const iconOffset = $(this).offset(); const iconOffset = $(this).offset();
if (iconOffset) { if (iconOffset) {
@@ -1207,7 +1207,7 @@ function attachDragHandlersToIcon($icon) {
$(this).addClass('rpg-hidden'); $(this).addClass('rpg-hidden');
$panel.fadeIn(200); $panel.fadeIn(200);
} else { } 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) { $icon.on('mousedown.thoughtIconDrag', function(e) {
if (window.innerWidth > 1000) return; if (window.innerWidth > 1000) return;
console.log('[Thought Icon] mousedown'); // console.log('[Thought Icon] mousedown');
e.preventDefault(); e.preventDefault();
mouseDown = true; mouseDown = true;
@@ -1237,7 +1237,7 @@ function attachDragHandlersToIcon($icon) {
if (!mouseDown || window.innerWidth > 1000) return; if (!mouseDown || window.innerWidth > 1000) return;
if (!touchMoved) { if (!touchMoved) {
console.log('[Thought Icon] mousemove - first movement'); // console.log('[Thought Icon] mousemove - first movement');
} }
touchMoved = true; touchMoved = true;
@@ -1280,7 +1280,7 @@ function attachDragHandlersToIcon($icon) {
$(document).on('mouseup.thoughtIconDrag', function(e) { $(document).on('mouseup.thoughtIconDrag', function(e) {
if (!mouseDown) return; if (!mouseDown) return;
console.log('[Thought Icon] mouseup - isDragging:', isDragging, 'touchMoved:', touchMoved); // console.log('[Thought Icon] mouseup - isDragging:', isDragging, 'touchMoved:', touchMoved);
mouseDown = false; mouseDown = false;
@@ -1568,22 +1568,82 @@ export function createThoughtPanel($message, thoughtsArray) {
// Load saved icon position in mobile, or default to center of viewport // Load saved icon position in mobile, or default to center of viewport
if (extensionSettings.thoughtIconPosition && extensionSettings.thoughtIconPosition.top && extensionSettings.thoughtIconPosition.left) { if (extensionSettings.thoughtIconPosition && extensionSettings.thoughtIconPosition.top && extensionSettings.thoughtIconPosition.left) {
const pos = extensionSettings.thoughtIconPosition; const pos = extensionSettings.thoughtIconPosition;
$thoughtIcon.css({
top: pos.top, // Validate saved position - check if it's not at the very top (likely invalid)
left: pos.left, const savedTop = parseInt(pos.top);
transform: 'none', const topBar = $('#top-settings-holder');
right: 'auto', const topBarHeight = topBar.length ? topBar.outerHeight() : 60;
bottom: 'auto'
}); // 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 { } else {
// Default position: center of viewport // Default position: center-right of viewport, accounting for top bar
$thoughtIcon.css({ // Use setTimeout to ensure DOM is fully rendered before calculating positions
top: '50%', setTimeout(() => {
left: '50%', const topBar = $('#top-settings-holder');
transform: 'translate(-50%, -50%)', const topBarHeight = topBar.length ? topBar.outerHeight() : 60;
right: 'auto', const viewportHeight = window.innerHeight;
bottom: 'auto' 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 { } else {
// Desktop: show panel, hide icon with class // Desktop: show panel, hide icon with class
+19 -19
View File
@@ -181,12 +181,12 @@ export function renderUserStats() {
// Don't render if no data exists (e.g., after cache clear) // Don't render if no data exists (e.g., after cache clear)
// Check both lastGeneratedData and committedTrackerData // Check both lastGeneratedData and committedTrackerData
console.log('[RPG UserStats Render] Checking data:', { // console.log('[RPG UserStats Render] Checking data:', {
hasLastGenerated: !!lastGeneratedData.userStats, // hasLastGenerated: !!lastGeneratedData.userStats,
hasCommitted: !!committedTrackerData.userStats, // hasCommitted: !!committedTrackerData.userStats,
lastGeneratedPreview: lastGeneratedData.userStats ? lastGeneratedData.userStats.substring(0, 100) : 'null', // lastGeneratedPreview: lastGeneratedData.userStats ? lastGeneratedData.userStats.substring(0, 100) : 'null',
committedPreview: committedTrackerData.userStats ? committedTrackerData.userStats.substring(0, 100) : 'null' // committedPreview: committedTrackerData.userStats ? committedTrackerData.userStats.substring(0, 100) : 'null'
}); // });
if (!lastGeneratedData.userStats && !committedTrackerData.userStats) { if (!lastGeneratedData.userStats && !committedTrackerData.userStats) {
// Always render to the #rpg-user-stats container (mobile layout just moves it around in DOM) // 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; const stats = extensionSettings.userStats;
console.log('[RPG UserStats Render] Current extensionSettings.userStats:', { // console.log('[RPG UserStats Render] Current extensionSettings.userStats:', {
health: stats.health, // health: stats.health,
satiety: stats.satiety, // satiety: stats.satiety,
energy: stats.energy, // energy: stats.energy,
hygiene: stats.hygiene, // hygiene: stats.hygiene,
arousal: stats.arousal, // arousal: stats.arousal,
mood: stats.mood, // mood: stats.mood,
conditions: stats.conditions // conditions: stats.conditions
}); // });
const config = extensionSettings.trackerConfig?.userStats || { const config = extensionSettings.trackerConfig?.userStats || {
customStats: [ customStats: [
{ id: 'health', name: 'Health', enabled: true }, { id: 'health', name: 'Health', enabled: true },
@@ -394,13 +394,13 @@ export function renderUserStats() {
html += '</div>'; // Close rpg-stats-content html += '</div>'; // Close rpg-stats-content
console.log('[RPG UserStats Render] Generated HTML length:', html.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] HTML preview:', html.substring(0, 300));
console.log('[RPG UserStats Render] Container exists:', !!$userStatsContainer, '$userStatsContainer length:', $userStatsContainer?.length); // 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) // Always render to the #rpg-user-stats container (mobile layout just moves it around in DOM)
$userStatsContainer.html(html); $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 // Add event listeners for editable stat values
$('.rpg-editable-stat').on('blur', function() { $('.rpg-editable-stat').on('blur', function() {
+4 -4
View File
@@ -1001,7 +1001,7 @@ export class EncounterModal {
{ clearChatInput: false } { clearChatInput: false }
); );
console.log(`[RPG Companion] Added combat summary to chat as "${speakerName}"`); // console.log(`[RPG Companion] Added combat summary to chat as "${speakerName}"`);
// Update combat over screen // Update combat over screen
this.updateCombatOverScreen(true, speakerName); this.updateCombatOverScreen(true, speakerName);
@@ -1259,7 +1259,7 @@ export class EncounterModal {
return; return;
} }
console.log('[RPG Companion] Regenerating request:', this.lastRequest.type); // console.log('[RPG Companion] Regenerating request:', this.lastRequest.type);
if (this.lastRequest.type === 'init') { if (this.lastRequest.type === 'init') {
// Retry initialization // Retry initialization
@@ -1327,7 +1327,7 @@ export class EncounterModal {
return; return;
} }
console.log('[RPG Companion] Regenerating request:', this.lastRequest.type); // console.log('[RPG Companion] Regenerating request:', this.lastRequest.type);
if (this.lastRequest.type === 'init') { if (this.lastRequest.type === 'init') {
// Retry initialization // Retry initialization
@@ -1369,7 +1369,7 @@ export class EncounterModal {
this.modal.setAttribute('data-weather', weather.toLowerCase()); this.modal.setAttribute('data-weather', weather.toLowerCase());
} }
console.log('[RPG Companion] Applied environment styling:', styleNotes); // console.log('[RPG Companion] Applied environment styling:', styleNotes);
} }
/** /**
+42 -42
View File
@@ -92,12 +92,12 @@ export function updateCollapseToggleIcon() {
const isOpen = $panel.hasClass('rpg-mobile-open'); const isOpen = $panel.hasClass('rpg-mobile-open');
const isLeftPanel = $panel.hasClass('rpg-position-left'); const isLeftPanel = $panel.hasClass('rpg-position-left');
console.log('[RPG Mobile] updateCollapseToggleIcon:', { // console.log('[RPG Mobile] updateCollapseToggleIcon:', {
isMobile: true, // isMobile: true,
isOpen, // isOpen,
isLeftPanel, // isLeftPanel,
settingIcon: isOpen ? (isLeftPanel ? 'chevron-left' : 'chevron-right') : (isLeftPanel ? 'chevron-right' : 'chevron-left') // settingIcon: isOpen ? (isLeftPanel ? 'chevron-left' : 'chevron-right') : (isLeftPanel ? 'chevron-right' : 'chevron-left')
}); // });
if (isLeftPanel) { if (isLeftPanel) {
if (isOpen) { if (isOpen) {
@@ -157,44 +157,44 @@ export function setupCollapseToggle() {
// On mobile: button toggles panel open/closed (same as desktop behavior) // On mobile: button toggles panel open/closed (same as desktop behavior)
if (isMobile) { if (isMobile) {
const isOpen = $panel.hasClass('rpg-mobile-open'); const isOpen = $panel.hasClass('rpg-mobile-open');
console.log('[RPG Mobile] Collapse toggle clicked. Current state:', { // console.log('[RPG Mobile] Collapse toggle clicked. Current state:', {
isOpen, // isOpen,
panelClasses: $panel.attr('class'), // panelClasses: $panel.attr('class'),
inlineStyles: $panel.attr('style'), // inlineStyles: $panel.attr('style'),
panelPosition: { // panelPosition: {
top: $panel.css('top'), // top: $panel.css('top'),
bottom: $panel.css('bottom'), // bottom: $panel.css('bottom'),
transform: $panel.css('transform'), // transform: $panel.css('transform'),
visibility: $panel.css('visibility') // visibility: $panel.css('visibility')
} // }
}); // });
if (isOpen) { if (isOpen) {
// Close panel with animation // Close panel with animation
console.log('[RPG Mobile] Closing panel'); // console.log('[RPG Mobile] Closing panel');
closeMobilePanelWithAnimation(); closeMobilePanelWithAnimation();
} else { } else {
// Open panel // Open panel
console.log('[RPG Mobile] Opening panel'); // console.log('[RPG Mobile] Opening panel');
$panel.addClass('rpg-mobile-open'); $panel.addClass('rpg-mobile-open');
const $overlay = $('<div class="rpg-mobile-overlay"></div>'); const $overlay = $('<div class="rpg-mobile-overlay"></div>');
$('body').append($overlay); $('body').append($overlay);
// Debug: Check state after animation should complete // Debug: Check state after animation should complete
setTimeout(() => { setTimeout(() => {
console.log('[RPG Mobile] 500ms after opening:', { // console.log('[RPG Mobile] 500ms after opening:', {
panelClasses: $panel.attr('class'), // panelClasses: $panel.attr('class'),
hasOpenClass: $panel.hasClass('rpg-mobile-open'), // hasOpenClass: $panel.hasClass('rpg-mobile-open'),
visibility: $panel.css('visibility'), // visibility: $panel.css('visibility'),
transform: $panel.css('transform'), // transform: $panel.css('transform'),
display: $panel.css('display'), // display: $panel.css('display'),
opacity: $panel.css('opacity') // opacity: $panel.css('opacity')
}); // });
}, 500); }, 500);
// Close when clicking overlay // Close when clicking overlay
$overlay.on('click', function() { $overlay.on('click', function() {
console.log('[RPG Mobile] Overlay clicked - closing panel'); // console.log('[RPG Mobile] Overlay clicked - closing panel');
closeMobilePanelWithAnimation(); closeMobilePanelWithAnimation();
updateCollapseToggleIcon(); updateCollapseToggleIcon();
}); });
@@ -203,20 +203,20 @@ export function setupCollapseToggle() {
// Update icon to reflect new state // Update icon to reflect new state
updateCollapseToggleIcon(); updateCollapseToggleIcon();
console.log('[RPG Mobile] After toggle:', { // console.log('[RPG Mobile] After toggle:', {
panelClasses: $panel.attr('class'), // panelClasses: $panel.attr('class'),
inlineStyles: $panel.attr('style'), // inlineStyles: $panel.attr('style'),
panelPosition: { // panelPosition: {
top: $panel.css('top'), // top: $panel.css('top'),
bottom: $panel.css('bottom'), // bottom: $panel.css('bottom'),
transform: $panel.css('transform'), // transform: $panel.css('transform'),
visibility: $panel.css('visibility') // visibility: $panel.css('visibility')
}, // },
gameContainer: { // gameContainer: {
opacity: $('.rpg-game-container').css('opacity'), // opacity: $('.rpg-game-container').css('opacity'),
visibility: $('.rpg-game-container').css('visibility') // visibility: $('.rpg-game-container').css('visibility')
} // }
}); // });
return; return;
} }
+51 -54
View File
@@ -55,13 +55,13 @@ export function setupMobileToggle() {
const $overlay = $('<div class="rpg-mobile-overlay"></div>'); const $overlay = $('<div class="rpg-mobile-overlay"></div>');
// DIAGNOSTIC: Check if elements exist and log setup state // DIAGNOSTIC: Check if elements exist and log setup state
console.log('[RPG Mobile] ========================================'); // console.log('[RPG Mobile] ========================================');
console.log('[RPG Mobile] setupMobileToggle called'); // console.log('[RPG Mobile] setupMobileToggle called');
console.log('[RPG Mobile] Button exists:', $mobileToggle.length > 0, 'jQuery object:', $mobileToggle); // console.log('[RPG Mobile] Button exists:', $mobileToggle.length > 0, 'jQuery object:', $mobileToggle);
console.log('[RPG Mobile] Panel exists:', $panel.length > 0); // console.log('[RPG Mobile] Panel exists:', $panel.length > 0);
console.log('[RPG Mobile] Window width:', window.innerWidth); // console.log('[RPG Mobile] Window width:', window.innerWidth);
console.log('[RPG Mobile] Is mobile viewport (<=1000):', window.innerWidth <= 1000); // console.log('[RPG Mobile] Is mobile viewport (<=1000):', window.innerWidth <= 1000);
console.log('[RPG Mobile] ========================================'); // console.log('[RPG Mobile] ========================================');
if ($mobileToggle.length === 0) { if ($mobileToggle.length === 0) {
console.error('[RPG Mobile] ERROR: Mobile toggle button not found in DOM!'); console.error('[RPG Mobile] ERROR: Mobile toggle button not found in DOM!');
@@ -72,7 +72,7 @@ export function setupMobileToggle() {
// Load and apply saved FAB position // Load and apply saved FAB position
if (extensionSettings.mobileFabPosition) { if (extensionSettings.mobileFabPosition) {
const pos = extensionSettings.mobileFabPosition; const pos = extensionSettings.mobileFabPosition;
console.log('[RPG Mobile] Loading saved FAB position:', pos); // console.log('[RPG Mobile] Loading saved FAB position:', pos);
// Apply saved position // Apply saved position
if (pos.top) $mobileToggle.css('top', pos.top); if (pos.top) $mobileToggle.css('top', pos.top);
@@ -250,7 +250,7 @@ export function setupMobileToggle() {
extensionSettings.mobileFabPosition = newPosition; extensionSettings.mobileFabPosition = newPosition;
saveSettings(); saveSettings();
console.log('[RPG Mobile] Saved new FAB position (mouse):', newPosition); // console.log('[RPG Mobile] Saved new FAB position (mouse):', newPosition);
// Constrain to viewport bounds (now that position is saved) // Constrain to viewport bounds (now that position is saved)
setTimeout(() => constrainFabToViewport(), 10); setTimeout(() => constrainFabToViewport(), 10);
@@ -291,7 +291,7 @@ export function setupMobileToggle() {
extensionSettings.mobileFabPosition = newPosition; extensionSettings.mobileFabPosition = newPosition;
saveSettings(); saveSettings();
console.log('[RPG Mobile] Saved new FAB position:', newPosition); // console.log('[RPG Mobile] Saved new FAB position:', newPosition);
// Constrain to viewport bounds (now that position is saved) // Constrain to viewport bounds (now that position is saved)
setTimeout(() => constrainFabToViewport(), 10); setTimeout(() => constrainFabToViewport(), 10);
@@ -304,7 +304,7 @@ export function setupMobileToggle() {
isDragging = false; isDragging = false;
} else { } else {
// Was a tap - toggle panel // Was a tap - toggle panel
console.log('[RPG Mobile] Quick tap detected - toggling panel'); // console.log('[RPG Mobile] Quick tap detected - toggling panel');
if ($panel.hasClass('rpg-mobile-open')) { if ($panel.hasClass('rpg-mobile-open')) {
// Close panel with animation // Close panel with animation
@@ -327,28 +327,28 @@ export function setupMobileToggle() {
$mobileToggle.on('click', function(e) { $mobileToggle.on('click', function(e) {
// Skip if we just finished dragging // Skip if we just finished dragging
if ($mobileToggle.data('just-dragged')) { if ($mobileToggle.data('just-dragged')) {
console.log('[RPG Mobile] Click blocked - just finished dragging'); // console.log('[RPG Mobile] Click blocked - just finished dragging');
return; return;
} }
console.log('[RPG Mobile] >>> CLICK EVENT FIRED <<<', { // console.log('[RPG Mobile] >>> CLICK EVENT FIRED <<<', {
windowWidth: window.innerWidth, // windowWidth: window.innerWidth,
isMobileViewport: window.innerWidth <= 1000, // isMobileViewport: window.innerWidth <= 1000,
panelOpen: $panel.hasClass('rpg-mobile-open') // panelOpen: $panel.hasClass('rpg-mobile-open')
}); // });
// Work on both mobile and desktop (removed viewport check) // Work on both mobile and desktop (removed viewport check)
if ($panel.hasClass('rpg-mobile-open')) { if ($panel.hasClass('rpg-mobile-open')) {
console.log('[RPG Mobile] Click: Closing panel'); // console.log('[RPG Mobile] Click: Closing panel');
closeMobilePanelWithAnimation(); closeMobilePanelWithAnimation();
} else { } else {
console.log('[RPG Mobile] Click: Opening panel'); // console.log('[RPG Mobile] Click: Opening panel');
$panel.addClass('rpg-mobile-open'); $panel.addClass('rpg-mobile-open');
$('body').append($overlay); $('body').append($overlay);
$mobileToggle.addClass('active'); $mobileToggle.addClass('active');
$overlay.on('click', function() { $overlay.on('click', function() {
console.log('[RPG Mobile] Overlay clicked - closing panel'); // console.log('[RPG Mobile] Overlay clicked - closing panel');
closeMobilePanelWithAnimation(); closeMobilePanelWithAnimation();
}); });
} }
@@ -367,7 +367,7 @@ export function setupMobileToggle() {
// Transitioning from desktop to mobile - handle immediately for smooth transition // Transitioning from desktop to mobile - handle immediately for smooth transition
if (!wasMobile && isMobile) { if (!wasMobile && isMobile) {
console.log('[RPG Mobile] Transitioning desktop -> mobile'); // console.log('[RPG Mobile] Transitioning desktop -> mobile');
// Show mobile toggle button // Show mobile toggle button
$mobileToggle.show(); $mobileToggle.show();
@@ -391,16 +391,16 @@ export function setupMobileToggle() {
// Clear any inline styles that might be overriding CSS // Clear any inline styles that might be overriding CSS
$panel.attr('style', ''); $panel.attr('style', '');
console.log('[RPG Mobile] After cleanup:', { // console.log('[RPG Mobile] After cleanup:', {
panelClasses: $panel.attr('class'), // panelClasses: $panel.attr('class'),
inlineStyles: $panel.attr('style'), // inlineStyles: $panel.attr('style'),
panelPosition: { // panelPosition: {
top: $panel.css('top'), // top: $panel.css('top'),
bottom: $panel.css('bottom'), // bottom: $panel.css('bottom'),
transform: $panel.css('transform'), // transform: $panel.css('transform'),
visibility: $panel.css('visibility') // visibility: $panel.css('visibility')
} // }
}); // });
// Set up mobile tabs IMMEDIATELY (no debounce delay) // Set up mobile tabs IMMEDIATELY (no debounce delay)
setupMobileTabs(); setupMobileTabs();
@@ -462,17 +462,14 @@ export function setupMobileToggle() {
// Clear any inline styles // Clear any inline styles
$panel.attr('style', ''); $panel.attr('style', '');
console.log('[RPG Mobile] Initial load on mobile viewport:', { // console.log('[RPG Mobile] Initial load on mobile viewport:', {
panelClasses: $panel.attr('class'), // panelClasses: $panel.attr('class'),
inlineStyles: $panel.attr('style'), // inlineStyles: $panel.attr('style'),
panelPosition: { // panelPosition: {
top: $panel.css('top'), // top: $panel.css('top'),
bottom: $panel.css('top'), // bottom: $panel.css('top'),
transform: $panel.css('transform'), // transform: $panel.css('transform'),
visibility: $panel.css('visibility') // visibility: $panel.css('visibility')\n // }\n // });\n setupMobileTabs();
}
});
setupMobileTabs();
// Set initial icon for mobile // Set initial icon for mobile
updateCollapseToggleIcon(); updateCollapseToggleIcon();
// Show mobile toggle on mobile viewport // Show mobile toggle on mobile viewport
@@ -491,7 +488,7 @@ export function setupMobileToggle() {
export function constrainFabToViewport() { export function constrainFabToViewport() {
// Only constrain if user has set a custom position // Only constrain if user has set a custom position
if (!extensionSettings.mobileFabPosition) { if (!extensionSettings.mobileFabPosition) {
console.log('[RPG Mobile] Skipping viewport constraint - using CSS defaults'); // console.log('[RPG Mobile] Skipping viewport constraint - using CSS defaults');
return; return;
} }
@@ -500,7 +497,7 @@ export function constrainFabToViewport() {
// Skip if button is not visible // Skip if button is not visible
if (!$mobileToggle.is(':visible')) { if (!$mobileToggle.is(':visible')) {
console.log('[RPG Mobile] Skipping viewport constraint - button not visible'); // console.log('[RPG Mobile] Skipping viewport constraint - button not visible');
return; return;
} }
@@ -530,12 +527,12 @@ export function constrainFabToViewport() {
// Only update if position changed // Only update if position changed
if (newX !== currentX || newY !== currentY) { if (newX !== currentX || newY !== currentY) {
console.log('[RPG Mobile] Constraining FAB to viewport:', { // console.log('[RPG Mobile] Constraining FAB to viewport:', {
old: { x: currentX, y: currentY }, // old: { x: currentX, y: currentY },
new: { x: newX, y: newY }, // new: { x: newX, y: newY },
viewport: { width: window.innerWidth, height: window.innerHeight }, // viewport: { width: window.innerWidth, height: window.innerHeight },
topBarHeight // topBarHeight
}); // });
// Apply new position // Apply new position
$mobileToggle.css({ $mobileToggle.css({
@@ -816,12 +813,12 @@ export function setupRefreshButtonDrag() {
return; return;
} }
console.log('[RPG Mobile] setupRefreshButtonDrag called'); // console.log('[RPG Mobile] setupRefreshButtonDrag called');
// Load and apply saved position // Load and apply saved position
if (extensionSettings.mobileRefreshPosition) { if (extensionSettings.mobileRefreshPosition) {
const pos = extensionSettings.mobileRefreshPosition; const pos = extensionSettings.mobileRefreshPosition;
console.log('[RPG Mobile] Loading saved refresh button position:', pos); // console.log('[RPG Mobile] Loading saved refresh button position:', pos);
// Apply saved position // Apply saved position
if (pos.top) $refreshBtn.css('top', pos.top); if (pos.top) $refreshBtn.css('top', pos.top);
@@ -1031,12 +1028,12 @@ export function setupDebugButtonDrag() {
return; return;
} }
console.log('[RPG Mobile] setupDebugButtonDrag called'); // console.log('[RPG Mobile] setupDebugButtonDrag called');
// Load and apply saved position // Load and apply saved position
if (extensionSettings.debugFabPosition) { if (extensionSettings.debugFabPosition) {
const pos = extensionSettings.debugFabPosition; const pos = extensionSettings.debugFabPosition;
console.log('[RPG Mobile] Loading saved debug button position:', pos); // console.log('[RPG Mobile] Loading saved debug button position:', pos);
// Apply saved position // Apply saved position
if (pos.top) $debugBtn.css('top', pos.top); if (pos.top) $debugBtn.css('top', pos.top);
+3 -3
View File
@@ -355,7 +355,7 @@ export function setupSettingsPopup() {
// Clear cache button // Clear cache button
$('#rpg-clear-cache').on('click', function() { $('#rpg-clear-cache').on('click', function() {
console.log('[RPG Companion] Clear Cache button clicked'); // console.log('[RPG Companion] Clear Cache button clicked');
// Clear the data (set to null so panels show "not generated yet") // Clear the data (set to null so panels show "not generated yet")
lastGeneratedData.userStats = null; lastGeneratedData.userStats = null;
@@ -375,7 +375,7 @@ export function setupSettingsPopup() {
const context = getContext(); const context = getContext();
if (context.chat_metadata && context.chat_metadata.rpg_companion) { if (context.chat_metadata && context.chat_metadata.rpg_companion) {
delete context.chat_metadata.rpg_companion; delete context.chat_metadata.rpg_companion;
console.log('[RPG Companion] Cleared chat_metadata.rpg_companion for current chat'); // console.log('[RPG Companion] Cleared chat_metadata.rpg_companion for current chat');
} }
// Clear all message swipe data // Clear all message swipe data
@@ -495,7 +495,7 @@ export function setupSettingsPopup() {
renderInventory(); renderInventory();
renderQuests(); renderQuests();
console.log('[RPG Companion] Cache cleared successfully'); // console.log('[RPG Companion] Cache cleared successfully');
}); });
return settingsModal; return settingsModal;
+2 -2
View File
@@ -243,7 +243,7 @@ function exportTrackerPreset() {
document.body.removeChild(link); document.body.removeChild(link);
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
console.log('[RPG Companion] Tracker preset exported successfully'); // console.log('[RPG Companion] Tracker preset exported successfully');
toastr.success(i18n.getTranslation('template.trackerEditorModal.messages.exportSuccess') || 'Tracker preset exported successfully!'); toastr.success(i18n.getTranslation('template.trackerEditorModal.messages.exportSuccess') || 'Tracker preset exported successfully!');
} catch (error) { } catch (error) {
console.error('[RPG Companion] Error exporting tracker preset:', error); console.error('[RPG Companion] Error exporting tracker preset:', error);
@@ -292,7 +292,7 @@ function importTrackerPreset() {
// Re-render the editor UI // Re-render the editor UI
renderEditorUI(); renderEditorUI();
console.log('[RPG Companion] Tracker preset imported successfully'); // console.log('[RPG Companion] Tracker preset imported successfully');
toastr.success(i18n.getTranslation('template.trackerEditorModal.messages.importSuccess') || 'Tracker preset imported successfully!'); toastr.success(i18n.getTranslation('template.trackerEditorModal.messages.importSuccess') || 'Tracker preset imported successfully!');
} catch (error) { } catch (error) {
console.error('[RPG Companion] Error importing tracker preset:', error); console.error('[RPG Companion] Error importing tracker preset:', error);
+9 -9
View File
@@ -357,7 +357,7 @@ export function migrateCharactersToJSON(textData) {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
export async function migrateToV3JSON() { export async function migrateToV3JSON() {
console.log('[RPG Migration] Starting migration to v3 JSON format...'); // console.log('[RPG Migration] Starting migration to v3 JSON format...');
const migrated = { const migrated = {
userStats: null, userStats: null,
@@ -367,28 +367,28 @@ export async function migrateToV3JSON() {
// Migrate User Stats // Migrate User Stats
if (committedTrackerData.userStats && typeof committedTrackerData.userStats === 'string') { if (committedTrackerData.userStats && typeof committedTrackerData.userStats === 'string') {
console.log('[RPG Migration] Migrating User Stats...'); // console.log('[RPG Migration] Migrating User Stats...');
migrated.userStats = migrateUserStatsToJSON(committedTrackerData.userStats); migrated.userStats = migrateUserStatsToJSON(committedTrackerData.userStats);
if (migrated.userStats) { if (migrated.userStats) {
console.log('[RPG Migration] ✓ User Stats migrated'); // console.log('[RPG Migration] ✓ User Stats migrated');
} }
} }
// Migrate Info Box // Migrate Info Box
if (committedTrackerData.infoBox && typeof committedTrackerData.infoBox === 'string') { if (committedTrackerData.infoBox && typeof committedTrackerData.infoBox === 'string') {
console.log('[RPG Migration] Migrating Info Box...'); // console.log('[RPG Migration] Migrating Info Box...');
migrated.infoBox = migrateInfoBoxToJSON(committedTrackerData.infoBox); migrated.infoBox = migrateInfoBoxToJSON(committedTrackerData.infoBox);
if (migrated.infoBox) { if (migrated.infoBox) {
console.log('[RPG Migration] ✓ Info Box migrated'); // console.log('[RPG Migration] ✓ Info Box migrated');
} }
} }
// Migrate Present Characters // Migrate Present Characters
if (committedTrackerData.characterThoughts && typeof committedTrackerData.characterThoughts === 'string') { if (committedTrackerData.characterThoughts && typeof committedTrackerData.characterThoughts === 'string') {
console.log('[RPG Migration] Migrating Present Characters...'); // console.log('[RPG Migration] Migrating Present Characters...');
migrated.characterThoughts = migrateCharactersToJSON(committedTrackerData.characterThoughts); migrated.characterThoughts = migrateCharactersToJSON(committedTrackerData.characterThoughts);
if (migrated.characterThoughts) { if (migrated.characterThoughts) {
console.log('[RPG Migration] ✓ Present Characters migrated'); // console.log('[RPG Migration] ✓ Present Characters migrated');
} }
} }
@@ -397,7 +397,7 @@ export async function migrateToV3JSON() {
// Initialize lockedItems if not present // Initialize lockedItems if not present
if (!extensionSettings.lockedItems) { if (!extensionSettings.lockedItems) {
console.log('[RPG Migration] Initializing lockedItems structure...'); // console.log('[RPG Migration] Initializing lockedItems structure...');
updateExtensionSettings({ updateExtensionSettings({
lockedItems: { lockedItems: {
stats: [], stats: [],
@@ -429,5 +429,5 @@ export async function migrateToV3JSON() {
await saveChatData(); await saveChatData();
await saveSettings(); await saveSettings();
console.log('[RPG Migration] ✅ Migration to v3 JSON format complete'); // console.log('[RPG Migration] ✅ Migration to v3 JSON format complete');
} }
+1 -1
View File
@@ -81,7 +81,7 @@ export function repairJSON(jsonString) {
const result = fn(); const result = fn();
// Validate it's actually an object or array // Validate it's actually an object or array
if (result && (typeof result === 'object')) { if (result && (typeof result === 'object')) {
console.log('[RPG JSON Repair] ✓ Repaired using Function constructor'); // console.log('[RPG JSON Repair] ✓ Repaired using Function constructor');
return result; return result;
} }
} catch (e) { } catch (e) {
+44 -7
View File
@@ -1616,22 +1616,43 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
} }
.rpg-location-text { .rpg-location-text {
font-size: clamp(0.625rem, 0.6vw, 0.75rem);
font-weight: bold; font-weight: bold;
color: var(--rpg-text); color: var(--rpg-text);
text-align: center; text-align: center;
line-height: 1.2; line-height: 1.2;
padding: 0; padding: 0.25em 0.5em;
margin: 0; margin: 0;
word-wrap: break-word; word-wrap: break-word;
overflow-wrap: break-word; overflow-wrap: break-word;
hyphens: auto; hyphens: auto;
flex: 1 1 auto; flex: 1 1 auto;
min-height: 0; min-height: 0;
overflow: hidden; max-height: 4em;
display: -webkit-box; width: 100%;
-webkit-line-clamp: 2; display: block;
-webkit-box-orient: vertical; overflow-y: auto;
overflow-x: hidden;
/* Dynamic text scaling based on content length */
font-size: clamp(0.45rem, calc(0.75rem - 0.005rem * var(--char-count, 0)), 0.75rem);
}
/* Custom scrollbar for location text */
.rpg-location-text::-webkit-scrollbar {
width: 4px;
}
.rpg-location-text::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
border-radius: 2px;
}
.rpg-location-text::-webkit-scrollbar-thumb {
background: var(--rpg-border);
border-radius: 2px;
}
.rpg-location-text::-webkit-scrollbar-thumb:hover {
background: var(--rpg-highlight);
} }
/* Row 3: Recent Events */ /* Row 3: Recent Events */
@@ -5122,9 +5143,13 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
left: auto !important; left: auto !important;
/* Mobile panel sizing */ /* Mobile panel sizing */
width: 85vw !important;
width: 85dvw !important; width: 85dvw !important;
max-width: 400px !important; max-width: 400px !important;
height: calc(100vh - var(--topBarBlockSize)) !important;
height: calc(100dvh - var(--topBarBlockSize)) !important; height: calc(100dvh - var(--topBarBlockSize)) !important;
max-height: calc(100vh - var(--topBarBlockSize)) !important;
max-height: calc(100dvh - var(--topBarBlockSize)) !important;
/* Hidden by default - completely removed from layout */ /* Hidden by default - completely removed from layout */
display: none !important; display: none !important;
@@ -5143,12 +5168,18 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
/* Allow collapse button to show outside panel */ /* Allow collapse button to show outside panel */
.rpg-game-container { .rpg-game-container {
overflow: visible !important; overflow: visible !important;
height: 100% !important;
max-height: 100% !important;
box-sizing: border-box !important;
padding: 0.75em !important;
} }
/* But keep content scrollable */ /* But keep content scrollable */
#rpg-panel-content { #rpg-panel-content {
overflow-y: auto !important; overflow-y: auto !important;
overflow-x: hidden !important; overflow-x: hidden !important;
max-height: 100% !important;
height: 100% !important;
} }
/* Show panel when opened with slide-in animation */ /* Show panel when opened with slide-in animation */
@@ -5317,10 +5348,12 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
max-height: 100%;
min-height: 0; min-height: 0;
margin: -12px -12px 16px -12px; margin: -12px -12px 16px -12px;
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: hidden;
box-sizing: border-box;
} }
/* Tab container at top of panel */ /* Tab container at top of panel */
@@ -5501,7 +5534,11 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
} }
.rpg-location-text { .rpg-location-text {
font-size: min(2.8vw, 0.875rem) !important; /* Dynamic text scaling based on content length - mobile override */
font-size: clamp(0.45rem, calc(0.875rem - 0.005rem * var(--char-count, 0)), 0.875rem) !important;
max-height: 4.5em !important;
overflow-y: auto !important;
overflow-x: hidden !important;
} }
.rpg-map-marker { .rpg-map-marker {
+6 -1
View File
@@ -935,9 +935,14 @@
<li>Fixed smaller bugs.</li> <li>Fixed smaller bugs.</li>
</ul> </ul>
<h4 style="margin-top: 20px; margin-bottom: 10px;"><strong data-i18n="settings.recommendedModels.title">Recommended Models:</strong></h4>
<p style="margin-left: 20px; line-height: 1.6;" data-i18n="settings.recommendedModels.description">
For the extension to work properly, **it is not recommended to use any models below 20B, especially if they're old.** It works best with the SOTA models such as Deepseek, Claude, GPT, or Gemini.
</p>
<h4 style="margin-top: 20px; margin-bottom: 10px;"><strong>Special thanks to all the other contributors for this project:</strong></h4> <h4 style="margin-top: 20px; margin-bottom: 10px;"><strong>Special thanks to all the other contributors for this project:</strong></h4>
<p style="margin-left: 20px; line-height: 1.6;"> <p style="margin-left: 20px; line-height: 1.6;">
Paperboygold, Munimunigamer, Subarashimo, Lilminzyu, Claude (???), IDeathByte, Chungchandev, Joenunezb, and Amauragis! Paperboygold, Munimunigamer, Subarashimo, Lilminzyu, Claude (???), IDeathByte, Chungchandev, Joenunezb, and Amauragis.
</p> </p>
<div style="margin-top: 20px; text-align: center;"> <div style="margin-top: 20px; text-align: center;">