2edb41ebe6
Add dashboard configuration to extensionSettings and create default layout system: State Management (state.js): - Added extensionSettings.dashboard with version 2 - gridConfig: columns (12), rowHeight (80px), gap (12px), snapToGrid, showGrid - tabs: Array of tab objects with widgets - defaultTab: ID of tab to show on load - Comprehensive inline documentation of structure Default Layout Generator (defaultLayout.js): - generateDefaultDashboard() - Creates 2-tab default layout - "Status" tab: userStats, infoBox, presentCharacters (3 widgets) - "Inventory" tab: inventory widget (1 widget) - migrateV1ToV2Dashboard() - Migrates v1.x settings to v2.0 - Respects user's visibility preferences (showUserStats, etc.) - Removes hidden widgets from migrated layout - Preserves user data during migration - validateDashboardConfig() - Validates dashboard structure - Utility functions: getWidgetCount(), findWidget() Persistence Layer (persistence.js): - Auto-migration on loadSettings() for existing users - Validates dashboard config on load - Regenerates default if config invalid or missing - Seamless backward compatibility Test Suite (defaultLayout.test.html): - 4 test scenarios with visual verification - Tests generation, validation, migration, utilities - Live dashboard JSON preview - Statistics panel (version, tabs, widgets, grid config) Features: - Automatic migration from v1.x hardcoded panel - Preserves user preferences during migration - Validates all dashboard configs on load - Generates sensible defaults for new users Acceptance Criteria Met: ✓ Dashboard config persists in extensionSettings ✓ Default layout generates on first load ✓ Existing users see migrated layout preserving their preferences ✓ All data structures validated Epic 1, Task 1.3 Complete (1-2 day estimate, <10 min actual)
275 lines
7.8 KiB
JavaScript
275 lines
7.8 KiB
JavaScript
/**
|
|
* Core State Management Module
|
|
* Centralizes all extension state variables
|
|
*/
|
|
|
|
// Type imports
|
|
/** @typedef {import('../types/inventory.js').InventoryV2} InventoryV2 */
|
|
|
|
/**
|
|
* 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
|
|
useSeparatePreset: false, // Use 'RPG Companion Trackers' preset for tracker generation instead of main API model
|
|
showUserStats: true,
|
|
showInfoBox: true,
|
|
showCharacterThoughts: true,
|
|
showInventory: true, // Show inventory section (v2 system)
|
|
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) + 20px)',
|
|
left: '12px'
|
|
}, // Saved position for mobile FAB button (top-left, stacked vertically)
|
|
mobileRefreshPosition: {
|
|
top: 'calc(var(--topBarBlockSize) + 80px)',
|
|
left: '12px'
|
|
}, // Saved position for mobile refresh button (below toggle button)
|
|
debugFabPosition: {
|
|
top: 'calc(var(--topBarBlockSize) + 140px)',
|
|
left: '12px'
|
|
}, // Saved position for debug FAB button (below refresh button)
|
|
userStats: {
|
|
health: 100,
|
|
satiety: 100,
|
|
energy: 100,
|
|
hygiene: 100,
|
|
arousal: 0,
|
|
mood: '😐',
|
|
conditions: 'None',
|
|
/** @type {InventoryV2} */
|
|
inventory: {
|
|
version: 2,
|
|
onPerson: "None",
|
|
stored: {},
|
|
assets: "None"
|
|
}
|
|
},
|
|
level: 1, // User's character level
|
|
classicStats: {
|
|
str: 10,
|
|
dex: 10,
|
|
con: 10,
|
|
int: 10,
|
|
wis: 10,
|
|
cha: 10
|
|
},
|
|
lastDiceRoll: null, // Store last dice roll result
|
|
collapsedInventoryLocations: [], // Array of collapsed storage location names
|
|
inventoryViewModes: {
|
|
onPerson: 'list', // 'list' or 'grid' view mode for On Person section
|
|
stored: 'list', // 'list' or 'grid' view mode for Stored section
|
|
assets: 'list' // 'list' or 'grid' view mode for Assets section
|
|
},
|
|
debugMode: false, // Enable debug logging visible in UI (for mobile debugging)
|
|
|
|
// Dashboard v2.0 Configuration
|
|
dashboard: {
|
|
version: 2, // Dashboard config version
|
|
|
|
gridConfig: {
|
|
columns: 12, // Grid columns
|
|
rowHeight: 80, // Pixels per row
|
|
gap: 12, // Gap between widgets (px)
|
|
snapToGrid: true, // Auto-snap enabled
|
|
showGrid: true // Show grid lines in edit mode
|
|
},
|
|
|
|
tabs: [
|
|
// Default tabs will be generated by generateDefaultDashboard()
|
|
// Structure:
|
|
// {
|
|
// id: 'tab-status',
|
|
// name: 'Status',
|
|
// icon: '📊',
|
|
// order: 0,
|
|
// widgets: [
|
|
// {
|
|
// id: 'widget-1',
|
|
// type: 'userStats',
|
|
// x: 0, y: 0, w: 6, h: 3,
|
|
// config: {}
|
|
// }
|
|
// ]
|
|
// }
|
|
],
|
|
|
|
defaultTab: 'tab-status' // Which tab to show on load
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 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;
|
|
|
|
/**
|
|
* Feature flags for gradual rollout of new features
|
|
*/
|
|
export const FEATURE_FLAGS = {
|
|
useNewInventory: true // Enable v2 inventory system with categorized storage
|
|
};
|
|
|
|
/**
|
|
* Debug logs storage for mobile-friendly debugging
|
|
* Stores parser logs that can be viewed in UI
|
|
*/
|
|
export let debugLogs = [];
|
|
|
|
/**
|
|
* 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;
|
|
export let $inventoryContainer = 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 getPendingDiceRoll() {
|
|
return pendingDiceRoll;
|
|
}
|
|
|
|
export function setPanelContainer($element) {
|
|
$panelContainer = $element;
|
|
}
|
|
|
|
export function setUserStatsContainer($element) {
|
|
$userStatsContainer = $element;
|
|
}
|
|
|
|
export function setInfoBoxContainer($element) {
|
|
$infoBoxContainer = $element;
|
|
}
|
|
|
|
export function setThoughtsContainer($element) {
|
|
$thoughtsContainer = $element;
|
|
}
|
|
|
|
export function setInventoryContainer($element) {
|
|
$inventoryContainer = $element;
|
|
}
|
|
|
|
export function addDebugLog(message, data = null) {
|
|
const timestamp = new Date().toISOString().split('T')[1].split('.')[0]; // HH:MM:SS
|
|
const logEntry = {
|
|
timestamp,
|
|
message,
|
|
data: data ? JSON.stringify(data, null, 2) : null
|
|
};
|
|
debugLogs.push(logEntry);
|
|
|
|
// Keep only last 100 entries to avoid memory issues
|
|
if (debugLogs.length > 100) {
|
|
debugLogs.shift();
|
|
}
|
|
}
|
|
|
|
export function clearDebugLogs() {
|
|
debugLogs = [];
|
|
}
|
|
|
|
export function getDebugLogs() {
|
|
return debugLogs;
|
|
}
|