Fix extension loading, enhance theming, add horizontal scrolling, improve emoji parsing, rename to Main Quests
This commit is contained in:
@@ -17,6 +17,11 @@ import {
|
||||
import { saveChatData } from '../../core/persistence.js';
|
||||
import { generateSeparateUpdatePrompt } from './promptBuilder.js';
|
||||
import { parseResponse, parseUserStats } from './parser.js';
|
||||
import { renderUserStats } from '../rendering/userStats.js';
|
||||
import { renderInfoBox } from '../rendering/infoBox.js';
|
||||
import { renderThoughts } from '../rendering/thoughts.js';
|
||||
import { renderInventory } from '../rendering/inventory.js';
|
||||
import { renderQuests } from '../rendering/quests.js';
|
||||
|
||||
// Store the original preset name to restore after tracker generation
|
||||
let originalPresetName = null;
|
||||
@@ -192,6 +197,7 @@ export async function updateRPGData(renderUserStats, renderInfoBox, renderThough
|
||||
renderInfoBox();
|
||||
renderThoughts();
|
||||
renderInventory();
|
||||
renderQuests();
|
||||
} else {
|
||||
// No assistant message to attach to - just update display
|
||||
if (parsedData.userStats) {
|
||||
@@ -201,6 +207,7 @@ export async function updateRPGData(renderUserStats, renderInfoBox, renderThough
|
||||
renderInfoBox();
|
||||
renderThoughts();
|
||||
renderInventory();
|
||||
renderQuests();
|
||||
}
|
||||
|
||||
// Save to chat metadata
|
||||
|
||||
@@ -7,6 +7,45 @@ import { extensionSettings, FEATURE_FLAGS, addDebugLog } from '../../core/state.
|
||||
import { saveSettings } from '../../core/persistence.js';
|
||||
import { extractInventory } from './inventoryParser.js';
|
||||
|
||||
/**
|
||||
* Helper to separate emoji from text in a string
|
||||
* Handles cases where there's no comma or space after emoji
|
||||
* @param {string} str - String potentially containing emoji followed by text
|
||||
* @returns {{emoji: string, text: string}} Separated emoji and text
|
||||
*/
|
||||
function separateEmojiFromText(str) {
|
||||
if (!str) return { emoji: '', text: '' };
|
||||
|
||||
str = str.trim();
|
||||
|
||||
// Regex to match emoji at the start (handles most emoji including compound ones)
|
||||
// This matches emoji sequences including skin tones, gender modifiers, etc.
|
||||
const emojiRegex = /^[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}\u{1F000}-\u{1F02F}\u{1F0A0}-\u{1F0FF}\u{1F100}-\u{1F64F}\u{1F680}-\u{1F6FF}\u{1F910}-\u{1F96B}\u{1F980}-\u{1F9E0}\u{FE00}-\u{FE0F}\u{200D}\u{20E3}]+/u;
|
||||
const emojiMatch = str.match(emojiRegex);
|
||||
|
||||
if (emojiMatch) {
|
||||
const emoji = emojiMatch[0];
|
||||
let text = str.substring(emoji.length).trim();
|
||||
|
||||
// Remove leading comma or space if present
|
||||
text = text.replace(/^[,\s]+/, '');
|
||||
|
||||
return { emoji, text };
|
||||
}
|
||||
|
||||
// No emoji found - check if there's a comma separator anyway
|
||||
const commaParts = str.split(',');
|
||||
if (commaParts.length >= 2) {
|
||||
return {
|
||||
emoji: commaParts[0].trim(),
|
||||
text: commaParts.slice(1).join(',').trim()
|
||||
};
|
||||
}
|
||||
|
||||
// No clear separation - return original as text
|
||||
return { emoji: '', text: str };
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to log to both console and debug logs array
|
||||
*/
|
||||
@@ -169,18 +208,36 @@ export function parseUserStats(statsText) {
|
||||
// Format 2: Status: [Emoji], [Conditions]
|
||||
// Format 3: [Emoji]: [Conditions] (legacy)
|
||||
// Format 4: Mood: [Emoji] - [Conditions]
|
||||
// Format 5: Status: [Emoji Conditions] (no separator - FIXED)
|
||||
let moodMatch = null;
|
||||
|
||||
// Try new format: Status: emoji, conditions
|
||||
const statusMatch = statsText.match(/Status:\s*(.+?),\s*(.+)/i);
|
||||
// Try new format: Status: emoji, conditions OR Status: emojiConditions
|
||||
const statusMatch = statsText.match(/Status:\s*(.+)/i);
|
||||
if (statusMatch) {
|
||||
moodMatch = [null, statusMatch[1].trim(), statusMatch[2].trim()];
|
||||
const statusContent = statusMatch[1].trim();
|
||||
const { emoji, text } = separateEmojiFromText(statusContent);
|
||||
if (emoji && text) {
|
||||
moodMatch = [null, emoji, text];
|
||||
} else if (statusContent.includes(',')) {
|
||||
// Fallback to comma split if emoji detection failed
|
||||
const parts = statusContent.split(',').map(p => p.trim());
|
||||
moodMatch = [null, parts[0], parts.slice(1).join(', ')];
|
||||
}
|
||||
}
|
||||
// Try alternative: Mood: emoji, conditions
|
||||
else {
|
||||
const moodAltMatch = statsText.match(/Mood:\s*(.+?)[,\-]\s*(.+)/i);
|
||||
|
||||
// Try alternative: Mood: emoji, conditions OR Mood: emojiConditions
|
||||
if (!moodMatch) {
|
||||
const moodAltMatch = statsText.match(/Mood:\s*(.+)/i);
|
||||
if (moodAltMatch) {
|
||||
moodMatch = [null, moodAltMatch[1].trim(), moodAltMatch[2].trim()];
|
||||
const moodContent = moodAltMatch[1].trim();
|
||||
const { emoji, text } = separateEmojiFromText(moodContent);
|
||||
if (emoji && text) {
|
||||
moodMatch = [null, emoji, text];
|
||||
} else if (moodContent.includes(',') || moodContent.includes('-')) {
|
||||
// Fallback to comma/dash split if emoji detection failed
|
||||
const parts = moodContent.split(/[,\-]/).map(p => p.trim());
|
||||
moodMatch = [null, parts[0], parts.slice(1).join(', ')];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,6 +302,28 @@ export function parseUserStats(statsText) {
|
||||
extensionSettings.userStats.conditions = moodMatch[2].trim(); // Conditions
|
||||
}
|
||||
|
||||
// Extract quests
|
||||
const mainQuestMatch = statsText.match(/Main Quests?:\s*(.+)/i);
|
||||
if (mainQuestMatch) {
|
||||
extensionSettings.quests.main = mainQuestMatch[1].trim();
|
||||
debugLog('[RPG Parser] Main quests extracted:', mainQuestMatch[1].trim());
|
||||
}
|
||||
|
||||
const optionalQuestsMatch = statsText.match(/Optional Quests:\s*(.+)/i);
|
||||
if (optionalQuestsMatch) {
|
||||
const questsText = optionalQuestsMatch[1].trim();
|
||||
if (questsText && questsText !== 'None') {
|
||||
// Split by comma and clean up
|
||||
extensionSettings.quests.optional = questsText
|
||||
.split(',')
|
||||
.map(q => q.trim())
|
||||
.filter(q => q && q !== 'None');
|
||||
} else {
|
||||
extensionSettings.quests.optional = [];
|
||||
}
|
||||
debugLog('[RPG Parser] Optional quests extracted:', extensionSettings.quests.optional);
|
||||
}
|
||||
|
||||
debugLog('[RPG Parser] Final userStats after parsing:', {
|
||||
health: extensionSettings.userStats.health,
|
||||
satiety: extensionSettings.userStats.satiety,
|
||||
|
||||
@@ -122,9 +122,13 @@ export function generateTrackerInstructions(includeHtmlPrompt = true, includeCon
|
||||
instructions += 'Assets: [Vehicles, property, major possessions, or "None"]\n';
|
||||
} else {
|
||||
// Legacy v1 format
|
||||
instructions += 'Inventory: [Clothing/Armor, Inventory Items (list of important items/none)]\n';
|
||||
instructions += 'Inventory: [Clothing/Armor, Inventory Items (list of important items, or "None")]\\n';
|
||||
}
|
||||
|
||||
// Add quests section
|
||||
instructions += 'Main Quests: [Short title of the currently active main quest (for example, "Save the world"), or "None"]\n';
|
||||
instructions += 'Optional Quests: [Short titles of the currently active optional quests (for example, "Find Zandik\'s book"), or "None"]\n';
|
||||
|
||||
instructions += '```\n\n';
|
||||
}
|
||||
|
||||
@@ -137,6 +141,7 @@ export function generateTrackerInstructions(includeHtmlPrompt = true, includeCon
|
||||
instructions += 'Temperature: [Temperature in °C]\n';
|
||||
instructions += 'Time: [Time Start → Time End]\n';
|
||||
instructions += 'Location: [Location]\n';
|
||||
instructions += 'Recent Events: [Up to three past events leading to the ongoing scene (short descriptors with no details, for example, "last-night date with Mary")]\n';
|
||||
instructions += '```\n\n';
|
||||
}
|
||||
|
||||
@@ -208,9 +213,23 @@ export function generateContextualSummary() {
|
||||
if (stats.inventory) {
|
||||
const inventorySummary = buildInventorySummary(stats.inventory);
|
||||
if (inventorySummary && inventorySummary !== 'None') {
|
||||
summary += `Inventory:\n${inventorySummary}\n`;
|
||||
summary += `${inventorySummary}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
// Add quests summary
|
||||
if (extensionSettings.quests) {
|
||||
if (extensionSettings.quests.main && extensionSettings.quests.main !== 'None') {
|
||||
summary += `Main Quests: ${extensionSettings.quests.main}\n`;
|
||||
}
|
||||
if (extensionSettings.quests.optional && extensionSettings.quests.optional.length > 0) {
|
||||
const optionalQuests = extensionSettings.quests.optional.filter(q => q && q !== 'None').join(', ');
|
||||
if (optionalQuests) {
|
||||
summary += `Optional Quests: ${optionalQuests}\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Include classic stats (attributes) and dice roll only if there was a dice roll
|
||||
if (extensionSettings.lastDiceRoll) {
|
||||
const classicStats = extensionSettings.classicStats;
|
||||
@@ -224,7 +243,7 @@ export function generateContextualSummary() {
|
||||
if (extensionSettings.showInfoBox && committedTrackerData.infoBox) {
|
||||
// Parse info box data - support both new and legacy formats
|
||||
const lines = committedTrackerData.infoBox.split('\n');
|
||||
let date = '', weather = '', temp = '', time = '', location = '';
|
||||
let date = '', weather = '', temp = '', time = '', location = '', recentEvents = '';
|
||||
|
||||
// console.log('[RPG Companion] 🔍 Parsing Info Box lines:', lines);
|
||||
|
||||
@@ -242,6 +261,8 @@ export function generateContextualSummary() {
|
||||
time = line.replace('Time:', '').trim();
|
||||
} else if (line.startsWith('Location:')) {
|
||||
location = line.replace('Location:', '').trim();
|
||||
} else if (line.startsWith('Recent Events:')) {
|
||||
recentEvents = line.replace('Recent Events:', '').trim();
|
||||
}
|
||||
// Legacy format with emojis (for backward compatibility)
|
||||
else if (line.includes('🗓️:')) {
|
||||
@@ -264,7 +285,7 @@ export function generateContextualSummary() {
|
||||
|
||||
// console.log('[RPG Companion] 🔍 Parsed values - date:', date, 'weather:', weather, 'temp:', temp, 'time:', time, 'location:', location);
|
||||
|
||||
if (date || weather || temp || time || location) {
|
||||
if (date || weather || temp || time || location || recentEvents) {
|
||||
summary += `Information:\n`;
|
||||
summary += `Scene: `;
|
||||
if (date) summary += `${date}`;
|
||||
@@ -272,7 +293,9 @@ export function generateContextualSummary() {
|
||||
if (time) summary += ` | ${time}`;
|
||||
if (weather) summary += ` | ${weather}`;
|
||||
if (temp) summary += ` | ${temp}`;
|
||||
summary += `\n\n`;
|
||||
summary += `\n`;
|
||||
if (recentEvents) summary += `Recent Events: ${recentEvents}\n`;
|
||||
summary += `\n`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,6 +340,22 @@ export function generateRPGPromptText() {
|
||||
} else {
|
||||
promptText += `Last ${userName}'s Stats:\nNone - this is the first update.\n\n`;
|
||||
}
|
||||
|
||||
// Add current quests to the previous data context
|
||||
if (extensionSettings.quests) {
|
||||
if (extensionSettings.quests.main && extensionSettings.quests.main !== 'None') {
|
||||
promptText += `Main Quests: ${extensionSettings.quests.main}\n`;
|
||||
} else {
|
||||
promptText += `Main Quests: None\n`;
|
||||
}
|
||||
if (extensionSettings.quests.optional && extensionSettings.quests.optional.length > 0) {
|
||||
const optionalQuests = extensionSettings.quests.optional.filter(q => q && q !== 'None').join(', ');
|
||||
promptText += `Optional Quests: ${optionalQuests || 'None'}\n`;
|
||||
} else {
|
||||
promptText += `Optional Quests: None\n`;
|
||||
}
|
||||
promptText += `\n`;
|
||||
}
|
||||
}
|
||||
|
||||
if (extensionSettings.showInfoBox) {
|
||||
|
||||
Reference in New Issue
Block a user