Merge pull request #12 from paperboygold/feature/defensive-initialization
fix(init): add defensive error handling for edge cases
This commit is contained in:
@@ -108,7 +108,7 @@ import {
|
||||
// Feature modules
|
||||
import { setupPlotButtons, sendPlotProgression } from './src/systems/features/plotProgression.js';
|
||||
import { setupClassicStatsButtons } from './src/systems/features/classicStats.js';
|
||||
import { ensureHtmlCleaningRegex } from './src/systems/features/htmlCleaning.js';
|
||||
import { ensureHtmlCleaningRegex, detectConflictingRegexScripts } from './src/systems/features/htmlCleaning.js';
|
||||
|
||||
// Integration modules
|
||||
import {
|
||||
@@ -418,18 +418,69 @@ async function initUI() {
|
||||
* Main initialization function.
|
||||
*/
|
||||
jQuery(async () => {
|
||||
try {
|
||||
console.log('[RPG Companion] Starting initialization...');
|
||||
|
||||
// Load settings with validation
|
||||
try {
|
||||
loadSettings();
|
||||
} catch (error) {
|
||||
console.error('[RPG Companion] Settings load failed, continuing with defaults:', error);
|
||||
}
|
||||
|
||||
// Add extension settings to Extensions tab
|
||||
try {
|
||||
await addExtensionSettings();
|
||||
} catch (error) {
|
||||
console.error('[RPG Companion] Failed to add extension settings tab:', error);
|
||||
// Don't throw - extension can still work without settings tab
|
||||
}
|
||||
|
||||
// Initialize UI
|
||||
try {
|
||||
await initUI();
|
||||
} catch (error) {
|
||||
console.error('[RPG Companion] UI initialization failed:', error);
|
||||
throw error; // This is critical - can't continue without UI
|
||||
}
|
||||
|
||||
// Load chat-specific data for current chat
|
||||
try {
|
||||
loadChatData();
|
||||
} catch (error) {
|
||||
console.error('[RPG Companion] Chat data load failed, using defaults:', error);
|
||||
}
|
||||
|
||||
// Import the HTML cleaning regex if needed
|
||||
try {
|
||||
await ensureHtmlCleaningRegex(st_extension_settings, saveSettingsDebounced);
|
||||
} catch (error) {
|
||||
console.error('[RPG Companion] HTML regex import failed:', error);
|
||||
// Non-critical - continue without it
|
||||
}
|
||||
|
||||
// Detect conflicting regex scripts from old manual formatters
|
||||
try {
|
||||
const conflicts = detectConflictingRegexScripts(st_extension_settings);
|
||||
if (conflicts.length > 0) {
|
||||
console.warn('[RPG Companion] ⚠️ Detected old manual formatting regex scripts that may conflict:');
|
||||
conflicts.forEach(name => console.warn(` - ${name}`));
|
||||
console.warn('[RPG Companion] Consider disabling these regexes as the extension now handles formatting automatically.');
|
||||
|
||||
// Show user-friendly warning (non-blocking)
|
||||
toastr.warning(
|
||||
`Found ${conflicts.length} old RPG formatting regex script(s). These may conflict with the extension. Check console for details.`,
|
||||
'RPG Companion Warning',
|
||||
{ timeOut: 8000 }
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[RPG Companion] Conflict detection failed:', error);
|
||||
// Non-critical - continue anyway
|
||||
}
|
||||
|
||||
// Register all event listeners
|
||||
try {
|
||||
registerAllEvents({
|
||||
[event_types.MESSAGE_SENT]: onMessageSent,
|
||||
[event_types.GENERATION_STARTED]: onGenerationStarted,
|
||||
@@ -439,10 +490,21 @@ jQuery(async () => {
|
||||
[event_types.USER_MESSAGE_RENDERED]: updatePersonaAvatar,
|
||||
[event_types.SETTINGS_UPDATED]: updatePersonaAvatar
|
||||
});
|
||||
|
||||
// console.log('[RPG Companion] Extension loaded successfully');
|
||||
} catch (error) {
|
||||
console.error('[RPG Companion] Failed to initialize:', error);
|
||||
throw error;
|
||||
console.error('[RPG Companion] Event registration failed:', error);
|
||||
throw error; // This is critical - can't continue without events
|
||||
}
|
||||
|
||||
console.log('[RPG Companion] ✅ Extension loaded successfully');
|
||||
} catch (error) {
|
||||
console.error('[RPG Companion] ❌ Critical initialization failure:', error);
|
||||
console.error('[RPG Companion] Error details:', error.message, error.stack);
|
||||
|
||||
// Show user-friendly error message
|
||||
toastr.error(
|
||||
'RPG Companion failed to initialize. Check console for details. Please try refreshing the page or resetting extension settings.',
|
||||
'RPG Companion Error',
|
||||
{ timeOut: 10000 }
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
+61
-2
@@ -18,13 +18,66 @@ import { migrateInventory } from '../utils/migration.js';
|
||||
|
||||
const extensionName = 'third-party/rpg-companion-sillytavern';
|
||||
|
||||
/**
|
||||
* Validates extension settings structure
|
||||
* @param {Object} settings - Settings object to validate
|
||||
* @returns {boolean} True if valid, false otherwise
|
||||
*/
|
||||
function validateSettings(settings) {
|
||||
if (!settings || typeof settings !== 'object') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for required top-level properties
|
||||
if (typeof settings.enabled !== 'boolean' ||
|
||||
typeof settings.autoUpdate !== 'boolean' ||
|
||||
!settings.userStats || typeof settings.userStats !== 'object') {
|
||||
console.warn('[RPG Companion] Settings validation failed: missing required properties');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate userStats structure
|
||||
const stats = settings.userStats;
|
||||
if (typeof stats.health !== 'number' ||
|
||||
typeof stats.satiety !== 'number' ||
|
||||
typeof stats.energy !== 'number') {
|
||||
console.warn('[RPG Companion] Settings validation failed: invalid userStats structure');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the extension settings from the global settings object.
|
||||
* Automatically migrates v1 inventory to v2 format if needed.
|
||||
*/
|
||||
export function loadSettings() {
|
||||
if (power_user.extensions && power_user.extensions[extensionName]) {
|
||||
updateExtensionSettings(power_user.extensions[extensionName]);
|
||||
try {
|
||||
// Validate power_user structure
|
||||
if (!power_user || typeof power_user !== 'object') {
|
||||
console.warn('[RPG Companion] power_user is not available, using default settings');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!power_user.extensions) {
|
||||
power_user.extensions = {};
|
||||
console.log('[RPG Companion] Created power_user.extensions object');
|
||||
}
|
||||
|
||||
if (power_user.extensions[extensionName]) {
|
||||
const savedSettings = power_user.extensions[extensionName];
|
||||
|
||||
// Validate loaded settings
|
||||
if (!validateSettings(savedSettings)) {
|
||||
console.warn('[RPG Companion] Loaded settings failed validation, using defaults');
|
||||
console.warn('[RPG Companion] Invalid settings:', savedSettings);
|
||||
// Save valid defaults to replace corrupt data
|
||||
saveSettings();
|
||||
return;
|
||||
}
|
||||
|
||||
updateExtensionSettings(savedSettings);
|
||||
// console.log('[RPG Companion] Settings loaded:', extensionSettings);
|
||||
} else {
|
||||
// console.log('[RPG Companion] No saved settings found, using defaults');
|
||||
@@ -39,6 +92,12 @@ export function loadSettings() {
|
||||
saveSettings(); // Persist migrated inventory
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[RPG Companion] Error loading settings:', error);
|
||||
console.error('[RPG Companion] Error details:', error.message, error.stack);
|
||||
console.warn('[RPG Companion] Using default settings due to load error');
|
||||
// Settings will remain at defaults from state.js
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,37 @@
|
||||
* Automatically imports HTML cleaning regex to strip HTML tags from outgoing prompts
|
||||
*/
|
||||
|
||||
/**
|
||||
* Detects old manual regex formatters that might conflict with the extension
|
||||
* @param {Object} st_extension_settings - SillyTavern extension settings object
|
||||
* @returns {Array<string>} Array of conflicting regex script names
|
||||
*/
|
||||
export function detectConflictingRegexScripts(st_extension_settings) {
|
||||
const conflictingNames = [
|
||||
'Format User\'s Stats (Only Output)',
|
||||
'Format User\'s Stats (Only Display)',
|
||||
'Format Info Box (Only Output)',
|
||||
'Format Info Box (Only Display)',
|
||||
'Format Character\'s Thoughts (Only Output)',
|
||||
'Format Character\'s Thoughts (Only Display)',
|
||||
'Format Character Thoughts (Only Output)',
|
||||
'Format Character Thoughts (Only Display)'
|
||||
];
|
||||
|
||||
const existingScripts = st_extension_settings?.regex || [];
|
||||
const conflicts = [];
|
||||
|
||||
for (const script of existingScripts) {
|
||||
if (script && script.scriptName && conflictingNames.some(name =>
|
||||
script.scriptName.toLowerCase().includes(name.toLowerCase())
|
||||
)) {
|
||||
conflicts.push(script.scriptName);
|
||||
}
|
||||
}
|
||||
|
||||
return conflicts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically imports the HTML cleaning regex script if it doesn't already exist.
|
||||
* This regex removes HTML tags from outgoing prompts to prevent formatting issues.
|
||||
@@ -11,10 +42,25 @@
|
||||
*/
|
||||
export async function ensureHtmlCleaningRegex(st_extension_settings, saveSettingsDebounced) {
|
||||
try {
|
||||
// Validate extension settings structure
|
||||
if (!st_extension_settings || typeof st_extension_settings !== 'object') {
|
||||
console.warn('[RPG Companion] Invalid extension_settings object, skipping HTML regex import');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the HTML cleaning regex already exists
|
||||
const scriptName = 'Clean HTML (From Outgoing Prompt)';
|
||||
const existingScripts = st_extension_settings?.regex || [];
|
||||
const alreadyExists = existingScripts.some(script => script.scriptName === scriptName);
|
||||
|
||||
// Validate regex array
|
||||
if (!Array.isArray(existingScripts)) {
|
||||
console.warn('[RPG Companion] extension_settings.regex is not an array, resetting to empty array');
|
||||
st_extension_settings.regex = [];
|
||||
}
|
||||
|
||||
const alreadyExists = existingScripts.some(script =>
|
||||
script && typeof script === 'object' && script.scriptName === scriptName
|
||||
);
|
||||
|
||||
if (alreadyExists) {
|
||||
console.log('[RPG Companion] HTML cleaning regex already exists, skipping import');
|
||||
@@ -55,11 +101,16 @@ export async function ensureHtmlCleaningRegex(st_extension_settings, saveSetting
|
||||
st_extension_settings.regex.push(regexScript);
|
||||
|
||||
// Save the changes using the already-imported function
|
||||
if (typeof saveSettingsDebounced === 'function') {
|
||||
saveSettingsDebounced();
|
||||
} else {
|
||||
console.warn('[RPG Companion] saveSettingsDebounced is not a function, cannot save HTML regex');
|
||||
}
|
||||
|
||||
console.log('[RPG Companion] ✅ HTML cleaning regex imported successfully');
|
||||
} catch (error) {
|
||||
console.error('[RPG Companion] Failed to import HTML cleaning regex:', error);
|
||||
console.error('[RPG Companion] Error details:', error.message, error.stack);
|
||||
// Don't throw - this is a nice-to-have feature
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2866,7 +2866,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
||||
background: var(--rpg-highlight, #e94560);
|
||||
color: white;
|
||||
border: 2px solid var(--rpg-bg, rgba(30, 30, 50, 0.95));
|
||||
font-size: 1.7vw;
|
||||
font-size: clamp(0.9rem, 1rem, 1.1rem);
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
@@ -2894,7 +2894,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.85vw;
|
||||
font-size: clamp(1.3rem, 1.4rem, 1.5rem);
|
||||
cursor: pointer;
|
||||
animation: thoughtIconPulse 2s ease-in-out infinite;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
|
||||
@@ -3016,7 +3016,7 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
||||
/* Thought content on the right */
|
||||
.rpg-thought-content {
|
||||
flex: 1;
|
||||
font-size: clamp(0.68vw, 0.7vw, 0.72vw);
|
||||
font-size: clamp(0.85rem, 0.9rem, 0.95rem);
|
||||
line-height: 1.5;
|
||||
color: var(--rpg-text, #eaeaea);
|
||||
font-style: italic;
|
||||
|
||||
Reference in New Issue
Block a user