refactor(core): extract core modules (state, persistence, config, events)
Extract core system modules from monolithic index.js into modular architecture: - src/core/state.js: All extension state variables with controlled setters - src/core/persistence.js: Settings and chat data persistence functions - src/core/config.js: Extension metadata and default configuration - src/core/events.js: SillyTavern event system wrapper Updated index.js to import and use new core modules. Removed ~220 lines of state/persistence code from index.js. Part of Epic 1: Foundation & Core Systems (Phase 1.1-1.2)
This commit is contained in:
@@ -3,99 +3,41 @@ import { eventSource, event_types, substituteParams, chat, generateRaw, saveSett
|
|||||||
import { selected_group, getGroupMembers } from '../../../group-chats.js';
|
import { selected_group, getGroupMembers } from '../../../group-chats.js';
|
||||||
import { power_user } from '../../../power-user.js';
|
import { power_user } from '../../../power-user.js';
|
||||||
|
|
||||||
const extensionName = 'third-party/rpg-companion-sillytavern';
|
// Core modules
|
||||||
|
import { extensionName, extensionFolderPath } from './src/core/config.js';
|
||||||
|
import {
|
||||||
|
extensionSettings,
|
||||||
|
lastGeneratedData,
|
||||||
|
committedTrackerData,
|
||||||
|
lastActionWasSwipe,
|
||||||
|
isGenerating,
|
||||||
|
isPlotProgression,
|
||||||
|
pendingDiceRoll,
|
||||||
|
FALLBACK_AVATAR_DATA_URI,
|
||||||
|
$panelContainer,
|
||||||
|
$userStatsContainer,
|
||||||
|
$infoBoxContainer,
|
||||||
|
$thoughtsContainer,
|
||||||
|
setExtensionSettings,
|
||||||
|
updateExtensionSettings,
|
||||||
|
setLastGeneratedData,
|
||||||
|
updateLastGeneratedData,
|
||||||
|
setCommittedTrackerData,
|
||||||
|
updateCommittedTrackerData,
|
||||||
|
setLastActionWasSwipe,
|
||||||
|
setIsGenerating,
|
||||||
|
setIsPlotProgression,
|
||||||
|
setPendingDiceRoll,
|
||||||
|
setPanelContainer,
|
||||||
|
setUserStatsContainer,
|
||||||
|
setInfoBoxContainer,
|
||||||
|
setThoughtsContainer
|
||||||
|
} from './src/core/state.js';
|
||||||
|
import { loadSettings, saveSettings, saveChatData, loadChatData, updateMessageSwipeData } from './src/core/persistence.js';
|
||||||
|
import { on as eventOn, event_types as coreEventTypes } from './src/core/events.js';
|
||||||
|
|
||||||
// Dynamically determine extension path based on current location
|
// Old state variable declarations removed - now imported from core modules
|
||||||
// This supports both global (public/extensions) and user-specific (data/default-user/extensions) installations
|
// (extensionSettings, lastGeneratedData, committedTrackerData, etc. are now in src/core/state.js)
|
||||||
const currentScriptPath = import.meta.url;
|
|
||||||
const isUserExtension = currentScriptPath.includes('/data/') || currentScriptPath.includes('\\data\\');
|
|
||||||
const extensionFolderPath = isUserExtension
|
|
||||||
? `data/default-user/extensions/${extensionName}`
|
|
||||||
: `scripts/extensions/${extensionName}`;
|
|
||||||
|
|
||||||
let extensionSettings = {
|
|
||||||
enabled: true,
|
|
||||||
autoUpdate: true,
|
|
||||||
updateDepth: 4, // How many messages to include in the context
|
|
||||||
generationMode: 'together', // 'separate' or 'together' - whether to generate with main response or separately
|
|
||||||
showUserStats: true,
|
|
||||||
showInfoBox: true,
|
|
||||||
showCharacterThoughts: true,
|
|
||||||
showThoughtsInChat: true, // Show thoughts overlay in chat
|
|
||||||
enableHtmlPrompt: false, // Enable immersive HTML prompt injection
|
|
||||||
enablePlotButtons: true, // Show plot progression buttons above chat input
|
|
||||||
panelPosition: 'right', // 'left', 'right', or 'top'
|
|
||||||
theme: 'default', // Theme: default, sci-fi, fantasy, cyberpunk, custom
|
|
||||||
customColors: {
|
|
||||||
bg: '#1a1a2e',
|
|
||||||
accent: '#16213e',
|
|
||||||
text: '#eaeaea',
|
|
||||||
highlight: '#e94560'
|
|
||||||
},
|
|
||||||
statBarColorLow: '#cc3333', // Color for low stat values (red)
|
|
||||||
statBarColorHigh: '#33cc66', // Color for high stat values (green)
|
|
||||||
enableAnimations: true, // Enable smooth animations for stats and content updates
|
|
||||||
mobileFabPosition: {
|
|
||||||
top: 'calc(var(--topBarBlockSize) + 60px)',
|
|
||||||
right: '12px'
|
|
||||||
}, // Saved position for mobile FAB button
|
|
||||||
userStats: {
|
|
||||||
health: 100,
|
|
||||||
satiety: 100,
|
|
||||||
energy: 100,
|
|
||||||
hygiene: 100,
|
|
||||||
arousal: 0,
|
|
||||||
mood: '😐',
|
|
||||||
conditions: 'None',
|
|
||||||
inventory: 'None'
|
|
||||||
},
|
|
||||||
classicStats: {
|
|
||||||
str: 10,
|
|
||||||
dex: 10,
|
|
||||||
con: 10,
|
|
||||||
int: 10,
|
|
||||||
wis: 10,
|
|
||||||
cha: 10
|
|
||||||
},
|
|
||||||
lastDiceRoll: null // Store last dice roll result
|
|
||||||
};
|
|
||||||
|
|
||||||
let lastGeneratedData = {
|
|
||||||
userStats: null,
|
|
||||||
infoBox: null,
|
|
||||||
characterThoughts: null,
|
|
||||||
html: null
|
|
||||||
};
|
|
||||||
|
|
||||||
// Tracks the "committed" tracker data that should be used as source for next generation
|
|
||||||
// This gets updated when user sends a new message or first time generation
|
|
||||||
let committedTrackerData = {
|
|
||||||
userStats: null,
|
|
||||||
infoBox: null,
|
|
||||||
characterThoughts: null
|
|
||||||
};
|
|
||||||
|
|
||||||
// Tracks whether the last action was a swipe (for separate mode)
|
|
||||||
// Used to determine whether to commit lastGeneratedData to committedTrackerData
|
|
||||||
let lastActionWasSwipe = false;
|
|
||||||
|
|
||||||
let isGenerating = false;
|
|
||||||
|
|
||||||
// Tracks if we're currently doing a plot progression
|
|
||||||
let isPlotProgression = false;
|
|
||||||
|
|
||||||
// Temporary storage for pending dice roll (not saved until user clicks "Save Roll")
|
|
||||||
let pendingDiceRoll = null;
|
|
||||||
|
|
||||||
// Fallback avatar image (base64-encoded SVG with "?" icon)
|
|
||||||
// Using base64 to avoid quote-encoding issues in HTML attributes
|
|
||||||
const FALLBACK_AVATAR_DATA_URI = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIj48cmVjdCB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgZmlsbD0iI2NjY2NjYyIgb3BhY2l0eT0iMC4zIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGR5PSIuM2VtIiBmaWxsPSIjNjY2IiBmb250LXNpemU9IjQwIj4/PC90ZXh0Pjwvc3ZnPg==';
|
|
||||||
|
|
||||||
// UI Elements
|
|
||||||
let $panelContainer = null;
|
|
||||||
let $userStatsContainer = null;
|
|
||||||
let $infoBoxContainer = null;
|
|
||||||
let $thoughtsContainer = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Safely attempts to get a thumbnail URL with proper error handling.
|
* Safely attempts to get a thumbnail URL with proper error handling.
|
||||||
@@ -137,126 +79,8 @@ function getSafeThumbnailUrl(type, filename) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Persistence functions removed - now imported from src/core/persistence.js
|
||||||
* Loads the extension settings from the global settings object.
|
// (loadSettings, saveSettings, saveChatData, loadChatData, updateMessageSwipeData)
|
||||||
*/
|
|
||||||
function loadSettings() {
|
|
||||||
if (power_user.extensions && power_user.extensions[extensionName]) {
|
|
||||||
Object.assign(extensionSettings, power_user.extensions[extensionName]);
|
|
||||||
// console.log('[RPG Companion] Settings loaded:', extensionSettings);
|
|
||||||
} else {
|
|
||||||
// console.log('[RPG Companion] No saved settings found, using defaults');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the extension settings to the global settings object.
|
|
||||||
*/
|
|
||||||
function saveSettings() {
|
|
||||||
if (!power_user.extensions) {
|
|
||||||
power_user.extensions = {};
|
|
||||||
}
|
|
||||||
power_user.extensions[extensionName] = extensionSettings;
|
|
||||||
saveSettingsDebounced();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves RPG data to the current chat's metadata.
|
|
||||||
*/
|
|
||||||
function saveChatData() {
|
|
||||||
if (!chat_metadata) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
chat_metadata.rpg_companion = {
|
|
||||||
userStats: extensionSettings.userStats,
|
|
||||||
classicStats: extensionSettings.classicStats,
|
|
||||||
lastGeneratedData: lastGeneratedData,
|
|
||||||
timestamp: Date.now()
|
|
||||||
};
|
|
||||||
|
|
||||||
saveChatDebounced();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the last assistant message's swipe data with current tracker data.
|
|
||||||
* This ensures user edits are preserved across swipes and included in generation context.
|
|
||||||
*/
|
|
||||||
function updateMessageSwipeData() {
|
|
||||||
const chat = getContext().chat;
|
|
||||||
if (!chat || chat.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the last assistant message
|
|
||||||
for (let i = chat.length - 1; i >= 0; i--) {
|
|
||||||
const message = chat[i];
|
|
||||||
if (!message.is_user) {
|
|
||||||
// Found last assistant message - update its swipe data
|
|
||||||
if (!message.extra) {
|
|
||||||
message.extra = {};
|
|
||||||
}
|
|
||||||
if (!message.extra.rpg_companion_swipes) {
|
|
||||||
message.extra.rpg_companion_swipes = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const swipeId = message.swipe_id || 0;
|
|
||||||
message.extra.rpg_companion_swipes[swipeId] = {
|
|
||||||
userStats: lastGeneratedData.userStats,
|
|
||||||
infoBox: lastGeneratedData.infoBox,
|
|
||||||
characterThoughts: lastGeneratedData.characterThoughts
|
|
||||||
};
|
|
||||||
|
|
||||||
// console.log('[RPG Companion] Updated message swipe data after user edit');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads RPG data from the current chat's metadata.
|
|
||||||
*/
|
|
||||||
function loadChatData() {
|
|
||||||
if (!chat_metadata || !chat_metadata.rpg_companion) {
|
|
||||||
// Reset to defaults if no data exists
|
|
||||||
extensionSettings.userStats = {
|
|
||||||
health: 100,
|
|
||||||
satiety: 100,
|
|
||||||
energy: 100,
|
|
||||||
hygiene: 100,
|
|
||||||
arousal: 0,
|
|
||||||
mood: '😐',
|
|
||||||
conditions: 'None',
|
|
||||||
inventory: 'None'
|
|
||||||
};
|
|
||||||
lastGeneratedData = {
|
|
||||||
userStats: null,
|
|
||||||
infoBox: null,
|
|
||||||
characterThoughts: null,
|
|
||||||
html: null
|
|
||||||
};
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const savedData = chat_metadata.rpg_companion;
|
|
||||||
|
|
||||||
// Restore stats
|
|
||||||
if (savedData.userStats) {
|
|
||||||
extensionSettings.userStats = { ...savedData.userStats };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore classic stats
|
|
||||||
if (savedData.classicStats) {
|
|
||||||
extensionSettings.classicStats = { ...savedData.classicStats };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore last generated data
|
|
||||||
if (savedData.lastGeneratedData) {
|
|
||||||
lastGeneratedData = { ...savedData.lastGeneratedData };
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log('[RPG Companion] Loaded chat data:', savedData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the selected theme to the panel.
|
* Applies the selected theme to the panel.
|
||||||
@@ -371,11 +195,11 @@ async function initUI() {
|
|||||||
`;
|
`;
|
||||||
$('body').append(mobileToggleHtml);
|
$('body').append(mobileToggleHtml);
|
||||||
|
|
||||||
// Cache UI elements
|
// Cache UI elements using state setters
|
||||||
$panelContainer = $('#rpg-companion-panel');
|
setPanelContainer($('#rpg-companion-panel'));
|
||||||
$userStatsContainer = $('#rpg-user-stats');
|
setUserStatsContainer($('#rpg-user-stats'));
|
||||||
$infoBoxContainer = $('#rpg-info-box');
|
setInfoBoxContainer($('#rpg-info-box'));
|
||||||
$thoughtsContainer = $('#rpg-thoughts');
|
setThoughtsContainer($('#rpg-thoughts'));
|
||||||
|
|
||||||
// Set up event listeners (enable/disable is handled in Extensions tab)
|
// Set up event listeners (enable/disable is handled in Extensions tab)
|
||||||
$('#rpg-toggle-auto-update').on('change', function() {
|
$('#rpg-toggle-auto-update').on('change', function() {
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* Core Configuration Module
|
||||||
|
* Extension metadata and configuration constants
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const extensionName = 'third-party/rpg-companion-sillytavern';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dynamically determine extension path based on current location
|
||||||
|
* This supports both global (public/extensions) and user-specific (data/default-user/extensions) installations
|
||||||
|
*/
|
||||||
|
const currentScriptPath = import.meta.url;
|
||||||
|
const isUserExtension = currentScriptPath.includes('/data/') || currentScriptPath.includes('\\data\\');
|
||||||
|
export const extensionFolderPath = isUserExtension
|
||||||
|
? `data/default-user/extensions/${extensionName}`
|
||||||
|
: `scripts/extensions/${extensionName}`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default extension settings
|
||||||
|
*/
|
||||||
|
export const defaultSettings = {
|
||||||
|
enabled: true,
|
||||||
|
autoUpdate: true,
|
||||||
|
updateDepth: 4, // How many messages to include in the context
|
||||||
|
generationMode: 'together', // 'separate' or 'together' - whether to generate with main response or separately
|
||||||
|
showUserStats: true,
|
||||||
|
showInfoBox: true,
|
||||||
|
showCharacterThoughts: true,
|
||||||
|
showThoughtsInChat: true, // Show thoughts overlay in chat
|
||||||
|
enableHtmlPrompt: false, // Enable immersive HTML prompt injection
|
||||||
|
enablePlotButtons: true, // Show plot progression buttons above chat input
|
||||||
|
panelPosition: 'right', // 'left', 'right', or 'top'
|
||||||
|
theme: 'default', // Theme: default, sci-fi, fantasy, cyberpunk, custom
|
||||||
|
customColors: {
|
||||||
|
bg: '#1a1a2e',
|
||||||
|
accent: '#16213e',
|
||||||
|
text: '#eaeaea',
|
||||||
|
highlight: '#e94560'
|
||||||
|
},
|
||||||
|
statBarColorLow: '#cc3333', // Color for low stat values (red)
|
||||||
|
statBarColorHigh: '#33cc66', // Color for high stat values (green)
|
||||||
|
enableAnimations: true, // Enable smooth animations for stats and content updates
|
||||||
|
mobileFabPosition: {
|
||||||
|
top: 'calc(var(--topBarBlockSize) + 60px)',
|
||||||
|
right: '12px'
|
||||||
|
}, // Saved position for mobile FAB button
|
||||||
|
userStats: {
|
||||||
|
health: 100,
|
||||||
|
satiety: 100,
|
||||||
|
energy: 100,
|
||||||
|
hygiene: 100,
|
||||||
|
arousal: 0,
|
||||||
|
mood: '😐',
|
||||||
|
conditions: 'None',
|
||||||
|
inventory: 'None'
|
||||||
|
},
|
||||||
|
classicStats: {
|
||||||
|
str: 10,
|
||||||
|
dex: 10,
|
||||||
|
con: 10,
|
||||||
|
int: 10,
|
||||||
|
wis: 10,
|
||||||
|
cha: 10
|
||||||
|
},
|
||||||
|
lastDiceRoll: null // Store last dice roll result
|
||||||
|
};
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Core Events Module
|
||||||
|
* Wrapper for SillyTavern event system
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { eventSource, event_types } from '../../../../../../script.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an event handler
|
||||||
|
* @param {string} eventType - Event type from event_types
|
||||||
|
* @param {Function} handler - Event handler function
|
||||||
|
*/
|
||||||
|
export function on(eventType, handler) {
|
||||||
|
eventSource.on(eventType, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a one-time event handler
|
||||||
|
* @param {string} eventType - Event type from event_types
|
||||||
|
* @param {Function} handler - Event handler function
|
||||||
|
*/
|
||||||
|
export function once(eventType, handler) {
|
||||||
|
eventSource.once(eventType, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an event handler
|
||||||
|
* @param {string} eventType - Event type from event_types
|
||||||
|
* @param {Function} handler - Event handler function to remove
|
||||||
|
*/
|
||||||
|
export function off(eventType, handler) {
|
||||||
|
eventSource.off(eventType, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit an event
|
||||||
|
* @param {string} eventType - Event type to emit
|
||||||
|
* @param {...*} args - Arguments to pass to handlers
|
||||||
|
*/
|
||||||
|
export function emit(eventType, ...args) {
|
||||||
|
eventSource.emit(eventType, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-export event types for convenience
|
||||||
|
*/
|
||||||
|
export { event_types };
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
/**
|
||||||
|
* Core Persistence Module
|
||||||
|
* Handles saving/loading extension settings and chat data
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { saveSettingsDebounced, chat_metadata, saveChatDebounced } from '../../../../../../script.js';
|
||||||
|
import { power_user } from '../../../../../power-user.js';
|
||||||
|
import { getContext } from '../../../../../extensions.js';
|
||||||
|
import {
|
||||||
|
extensionSettings,
|
||||||
|
lastGeneratedData,
|
||||||
|
setExtensionSettings,
|
||||||
|
updateExtensionSettings,
|
||||||
|
setLastGeneratedData
|
||||||
|
} from './state.js';
|
||||||
|
|
||||||
|
const extensionName = 'third-party/rpg-companion-sillytavern';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the extension settings from the global settings object.
|
||||||
|
*/
|
||||||
|
export function loadSettings() {
|
||||||
|
if (power_user.extensions && power_user.extensions[extensionName]) {
|
||||||
|
updateExtensionSettings(power_user.extensions[extensionName]);
|
||||||
|
// console.log('[RPG Companion] Settings loaded:', extensionSettings);
|
||||||
|
} else {
|
||||||
|
// console.log('[RPG Companion] No saved settings found, using defaults');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the extension settings to the global settings object.
|
||||||
|
*/
|
||||||
|
export function saveSettings() {
|
||||||
|
if (!power_user.extensions) {
|
||||||
|
power_user.extensions = {};
|
||||||
|
}
|
||||||
|
power_user.extensions[extensionName] = extensionSettings;
|
||||||
|
saveSettingsDebounced();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves RPG data to the current chat's metadata.
|
||||||
|
*/
|
||||||
|
export function saveChatData() {
|
||||||
|
if (!chat_metadata) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
chat_metadata.rpg_companion = {
|
||||||
|
userStats: extensionSettings.userStats,
|
||||||
|
classicStats: extensionSettings.classicStats,
|
||||||
|
lastGeneratedData: lastGeneratedData,
|
||||||
|
timestamp: Date.now()
|
||||||
|
};
|
||||||
|
|
||||||
|
saveChatDebounced();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the last assistant message's swipe data with current tracker data.
|
||||||
|
* This ensures user edits are preserved across swipes and included in generation context.
|
||||||
|
*/
|
||||||
|
export function updateMessageSwipeData() {
|
||||||
|
const chat = getContext().chat;
|
||||||
|
if (!chat || chat.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the last assistant message
|
||||||
|
for (let i = chat.length - 1; i >= 0; i--) {
|
||||||
|
const message = chat[i];
|
||||||
|
if (!message.is_user) {
|
||||||
|
// Found last assistant message - update its swipe data
|
||||||
|
if (!message.extra) {
|
||||||
|
message.extra = {};
|
||||||
|
}
|
||||||
|
if (!message.extra.rpg_companion_swipes) {
|
||||||
|
message.extra.rpg_companion_swipes = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const swipeId = message.swipe_id || 0;
|
||||||
|
message.extra.rpg_companion_swipes[swipeId] = {
|
||||||
|
userStats: lastGeneratedData.userStats,
|
||||||
|
infoBox: lastGeneratedData.infoBox,
|
||||||
|
characterThoughts: lastGeneratedData.characterThoughts
|
||||||
|
};
|
||||||
|
|
||||||
|
// console.log('[RPG Companion] Updated message swipe data after user edit');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads RPG data from the current chat's metadata.
|
||||||
|
*/
|
||||||
|
export function loadChatData() {
|
||||||
|
if (!chat_metadata || !chat_metadata.rpg_companion) {
|
||||||
|
// Reset to defaults if no data exists
|
||||||
|
updateExtensionSettings({
|
||||||
|
userStats: {
|
||||||
|
health: 100,
|
||||||
|
satiety: 100,
|
||||||
|
energy: 100,
|
||||||
|
hygiene: 100,
|
||||||
|
arousal: 0,
|
||||||
|
mood: '😐',
|
||||||
|
conditions: 'None',
|
||||||
|
inventory: 'None'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setLastGeneratedData({
|
||||||
|
userStats: null,
|
||||||
|
infoBox: null,
|
||||||
|
characterThoughts: null,
|
||||||
|
html: null
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const savedData = chat_metadata.rpg_companion;
|
||||||
|
|
||||||
|
// Restore stats
|
||||||
|
if (savedData.userStats) {
|
||||||
|
extensionSettings.userStats = { ...savedData.userStats };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore classic stats
|
||||||
|
if (savedData.classicStats) {
|
||||||
|
extensionSettings.classicStats = { ...savedData.classicStats };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore last generated data
|
||||||
|
if (savedData.lastGeneratedData) {
|
||||||
|
setLastGeneratedData({ ...savedData.lastGeneratedData });
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log('[RPG Companion] Loaded chat data:', savedData);
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
/**
|
||||||
|
* Core State Management Module
|
||||||
|
* Centralizes all extension state variables
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extension settings - persisted to SillyTavern settings
|
||||||
|
*/
|
||||||
|
export let extensionSettings = {
|
||||||
|
enabled: true,
|
||||||
|
autoUpdate: true,
|
||||||
|
updateDepth: 4, // How many messages to include in the context
|
||||||
|
generationMode: 'together', // 'separate' or 'together' - whether to generate with main response or separately
|
||||||
|
showUserStats: true,
|
||||||
|
showInfoBox: true,
|
||||||
|
showCharacterThoughts: true,
|
||||||
|
showThoughtsInChat: true, // Show thoughts overlay in chat
|
||||||
|
enableHtmlPrompt: false, // Enable immersive HTML prompt injection
|
||||||
|
enablePlotButtons: true, // Show plot progression buttons above chat input
|
||||||
|
panelPosition: 'right', // 'left', 'right', or 'top'
|
||||||
|
theme: 'default', // Theme: default, sci-fi, fantasy, cyberpunk, custom
|
||||||
|
customColors: {
|
||||||
|
bg: '#1a1a2e',
|
||||||
|
accent: '#16213e',
|
||||||
|
text: '#eaeaea',
|
||||||
|
highlight: '#e94560'
|
||||||
|
},
|
||||||
|
statBarColorLow: '#cc3333', // Color for low stat values (red)
|
||||||
|
statBarColorHigh: '#33cc66', // Color for high stat values (green)
|
||||||
|
enableAnimations: true, // Enable smooth animations for stats and content updates
|
||||||
|
mobileFabPosition: {
|
||||||
|
top: 'calc(var(--topBarBlockSize) + 60px)',
|
||||||
|
right: '12px'
|
||||||
|
}, // Saved position for mobile FAB button
|
||||||
|
userStats: {
|
||||||
|
health: 100,
|
||||||
|
satiety: 100,
|
||||||
|
energy: 100,
|
||||||
|
hygiene: 100,
|
||||||
|
arousal: 0,
|
||||||
|
mood: '😐',
|
||||||
|
conditions: 'None',
|
||||||
|
inventory: 'None'
|
||||||
|
},
|
||||||
|
classicStats: {
|
||||||
|
str: 10,
|
||||||
|
dex: 10,
|
||||||
|
con: 10,
|
||||||
|
int: 10,
|
||||||
|
wis: 10,
|
||||||
|
cha: 10
|
||||||
|
},
|
||||||
|
lastDiceRoll: null // Store last dice roll result
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last generated data from AI response
|
||||||
|
*/
|
||||||
|
export let lastGeneratedData = {
|
||||||
|
userStats: null,
|
||||||
|
infoBox: null,
|
||||||
|
characterThoughts: null,
|
||||||
|
html: null
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks the "committed" tracker data that should be used as source for next generation
|
||||||
|
* This gets updated when user sends a new message or first time generation
|
||||||
|
*/
|
||||||
|
export let committedTrackerData = {
|
||||||
|
userStats: null,
|
||||||
|
infoBox: null,
|
||||||
|
characterThoughts: null
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks whether the last action was a swipe (for separate mode)
|
||||||
|
* Used to determine whether to commit lastGeneratedData to committedTrackerData
|
||||||
|
*/
|
||||||
|
export let lastActionWasSwipe = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag indicating if generation is in progress
|
||||||
|
*/
|
||||||
|
export let isGenerating = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tracks if we're currently doing a plot progression
|
||||||
|
*/
|
||||||
|
export let isPlotProgression = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Temporary storage for pending dice roll (not saved until user clicks "Save Roll")
|
||||||
|
*/
|
||||||
|
export let pendingDiceRoll = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fallback avatar image (base64-encoded SVG with "?" icon)
|
||||||
|
* Using base64 to avoid quote-encoding issues in HTML attributes
|
||||||
|
*/
|
||||||
|
export const FALLBACK_AVATAR_DATA_URI = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDAiIGhlaWdodD0iMTAwIj48cmVjdCB3aWR0aD0iMTAwIiBoZWlnaHQ9IjEwMCIgZmlsbD0iI2NjY2NjYyIgb3BhY2l0eT0iMC4zIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGR5PSIuM2VtIiBmaWxsPSIjNjY2IiBmb250LXNpemU9IjQwIj4/PC90ZXh0Pjwvc3ZnPg==';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UI Element References (jQuery objects)
|
||||||
|
*/
|
||||||
|
export let $panelContainer = null;
|
||||||
|
export let $userStatsContainer = null;
|
||||||
|
export let $infoBoxContainer = null;
|
||||||
|
export let $thoughtsContainer = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State setters - provide controlled mutation of state variables
|
||||||
|
*/
|
||||||
|
export function setExtensionSettings(newSettings) {
|
||||||
|
extensionSettings = newSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateExtensionSettings(updates) {
|
||||||
|
Object.assign(extensionSettings, updates);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setLastGeneratedData(data) {
|
||||||
|
lastGeneratedData = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateLastGeneratedData(updates) {
|
||||||
|
Object.assign(lastGeneratedData, updates);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setCommittedTrackerData(data) {
|
||||||
|
committedTrackerData = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateCommittedTrackerData(updates) {
|
||||||
|
Object.assign(committedTrackerData, updates);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setLastActionWasSwipe(value) {
|
||||||
|
lastActionWasSwipe = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setIsGenerating(value) {
|
||||||
|
isGenerating = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setIsPlotProgression(value) {
|
||||||
|
isPlotProgression = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setPendingDiceRoll(roll) {
|
||||||
|
pendingDiceRoll = roll;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setPanelContainer($element) {
|
||||||
|
$panelContainer = $element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setUserStatsContainer($element) {
|
||||||
|
$userStatsContainer = $element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setInfoBoxContainer($element) {
|
||||||
|
$infoBoxContainer = $element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setThoughtsContainer($element) {
|
||||||
|
$thoughtsContainer = $element;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user