Merge branch 'pr-88'
This commit is contained in:
@@ -126,6 +126,9 @@ export function loadSettings() {
|
|||||||
migrateToTrackerConfig();
|
migrateToTrackerConfig();
|
||||||
saveSettings(); // Persist migration
|
saveSettings(); // Persist migration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Migrate to preset manager system if presets don't exist
|
||||||
|
migrateToPresetManager();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[RPG Companion] Error loading settings:', error);
|
console.error('[RPG Companion] Error loading settings:', error);
|
||||||
console.error('[RPG Companion] Error details:', error.message, error.stack);
|
console.error('[RPG Companion] Error details:', error.message, error.stack);
|
||||||
@@ -606,3 +609,397 @@ function migrateToTrackerConfig() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Preset Management Functions
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the entity key for the current character or group
|
||||||
|
* @returns {string|null} Entity key in format "char_{id}" or "group_{id}", or null if no character selected
|
||||||
|
*/
|
||||||
|
export function getCurrentEntityKey() {
|
||||||
|
const context = getContext();
|
||||||
|
if (context.groupId) {
|
||||||
|
return `group_${context.groupId}`;
|
||||||
|
} else if (context.characterId !== undefined && context.characterId !== null) {
|
||||||
|
return `char_${context.characterId}`;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the display name for the current character or group
|
||||||
|
* @returns {string} Display name for the current entity
|
||||||
|
*/
|
||||||
|
export function getCurrentEntityName() {
|
||||||
|
const context = getContext();
|
||||||
|
if (context.groupId) {
|
||||||
|
const group = context.groups?.find(g => g.id === context.groupId);
|
||||||
|
return group?.name || 'Group Chat';
|
||||||
|
} else if (context.characterId !== undefined && context.characterId !== null) {
|
||||||
|
return context.name2 || 'Character';
|
||||||
|
}
|
||||||
|
return 'No Character';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrates existing trackerConfig to the preset system if presetManager doesn't exist
|
||||||
|
* Creates a "Default" preset from the current trackerConfig
|
||||||
|
*/
|
||||||
|
export function migrateToPresetManager() {
|
||||||
|
if (!extensionSettings.presetManager || Object.keys(extensionSettings.presetManager.presets || {}).length === 0) {
|
||||||
|
// console.log('[RPG Companion] Migrating to preset manager system');
|
||||||
|
|
||||||
|
// Initialize presetManager if it doesn't exist
|
||||||
|
if (!extensionSettings.presetManager) {
|
||||||
|
extensionSettings.presetManager = {
|
||||||
|
presets: {},
|
||||||
|
characterAssociations: {},
|
||||||
|
activePresetId: null,
|
||||||
|
defaultPresetId: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create default preset from existing trackerConfig
|
||||||
|
const defaultPresetId = 'preset_default';
|
||||||
|
extensionSettings.presetManager.presets[defaultPresetId] = {
|
||||||
|
id: defaultPresetId,
|
||||||
|
name: 'Default',
|
||||||
|
trackerConfig: JSON.parse(JSON.stringify(extensionSettings.trackerConfig))
|
||||||
|
};
|
||||||
|
extensionSettings.presetManager.activePresetId = defaultPresetId;
|
||||||
|
extensionSettings.presetManager.defaultPresetId = defaultPresetId;
|
||||||
|
|
||||||
|
// console.log('[RPG Companion] Created Default preset from existing trackerConfig');
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all available presets
|
||||||
|
* @returns {Object} Map of preset ID to preset data
|
||||||
|
*/
|
||||||
|
export function getPresets() {
|
||||||
|
return extensionSettings.presetManager?.presets || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a specific preset by ID
|
||||||
|
* @param {string} presetId - The preset ID
|
||||||
|
* @returns {Object|null} The preset object or null if not found
|
||||||
|
*/
|
||||||
|
export function getPreset(presetId) {
|
||||||
|
return extensionSettings.presetManager?.presets?.[presetId] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the currently active preset ID
|
||||||
|
* @returns {string|null} The active preset ID or null
|
||||||
|
*/
|
||||||
|
export function getActivePresetId() {
|
||||||
|
return extensionSettings.presetManager?.activePresetId || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the default preset ID
|
||||||
|
* @returns {string|null} The default preset ID or null
|
||||||
|
*/
|
||||||
|
export function getDefaultPresetId() {
|
||||||
|
return extensionSettings.presetManager?.defaultPresetId || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a preset as the default
|
||||||
|
* @param {string} presetId - The preset ID to set as default
|
||||||
|
*/
|
||||||
|
export function setDefaultPreset(presetId) {
|
||||||
|
if (extensionSettings.presetManager.presets[presetId]) {
|
||||||
|
extensionSettings.presetManager.defaultPresetId = presetId;
|
||||||
|
saveSettings();
|
||||||
|
// console.log(`[RPG Companion] Set preset ${presetId} as default`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given preset is the default
|
||||||
|
* @param {string} presetId - The preset ID to check
|
||||||
|
* @returns {boolean} True if it's the default preset
|
||||||
|
*/
|
||||||
|
export function isDefaultPreset(presetId) {
|
||||||
|
return extensionSettings.presetManager?.defaultPresetId === presetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new preset from the current trackerConfig
|
||||||
|
* @param {string} name - Name for the new preset
|
||||||
|
* @returns {string} The ID of the newly created preset
|
||||||
|
*/
|
||||||
|
export function createPreset(name) {
|
||||||
|
const presetId = `preset_${Date.now()}`;
|
||||||
|
extensionSettings.presetManager.presets[presetId] = {
|
||||||
|
id: presetId,
|
||||||
|
name: name,
|
||||||
|
trackerConfig: JSON.parse(JSON.stringify(extensionSettings.trackerConfig))
|
||||||
|
};
|
||||||
|
// Also set it as the active preset so edits go to the new preset
|
||||||
|
extensionSettings.presetManager.activePresetId = presetId;
|
||||||
|
saveSettings();
|
||||||
|
// console.log(`[RPG Companion] Created preset "${name}" with ID ${presetId}`);
|
||||||
|
return presetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the current trackerConfig to the specified preset
|
||||||
|
* @param {string} presetId - The preset ID to save to
|
||||||
|
*/
|
||||||
|
export function saveToPreset(presetId) {
|
||||||
|
const preset = extensionSettings.presetManager.presets[presetId];
|
||||||
|
if (preset) {
|
||||||
|
preset.trackerConfig = JSON.parse(JSON.stringify(extensionSettings.trackerConfig));
|
||||||
|
saveSettings();
|
||||||
|
// console.log(`[RPG Companion] Saved current config to preset "${preset.name}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a preset's trackerConfig as the active configuration
|
||||||
|
* @param {string} presetId - The preset ID to load
|
||||||
|
* @returns {boolean} True if loaded successfully, false otherwise
|
||||||
|
*/
|
||||||
|
export function loadPreset(presetId) {
|
||||||
|
const preset = extensionSettings.presetManager.presets[presetId];
|
||||||
|
if (preset && preset.trackerConfig) {
|
||||||
|
extensionSettings.trackerConfig = JSON.parse(JSON.stringify(preset.trackerConfig));
|
||||||
|
extensionSettings.presetManager.activePresetId = presetId;
|
||||||
|
saveSettings();
|
||||||
|
// console.log(`[RPG Companion] Loaded preset "${preset.name}"`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renames a preset
|
||||||
|
* @param {string} presetId - The preset ID to rename
|
||||||
|
* @param {string} newName - The new name for the preset
|
||||||
|
*/
|
||||||
|
export function renamePreset(presetId, newName) {
|
||||||
|
const preset = extensionSettings.presetManager.presets[presetId];
|
||||||
|
if (preset) {
|
||||||
|
preset.name = newName;
|
||||||
|
saveSettings();
|
||||||
|
// console.log(`[RPG Companion] Renamed preset to "${newName}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a preset
|
||||||
|
* @param {string} presetId - The preset ID to delete
|
||||||
|
* @returns {boolean} True if deleted, false if it's the last preset (can't delete)
|
||||||
|
*/
|
||||||
|
export function deletePreset(presetId) {
|
||||||
|
const presets = extensionSettings.presetManager.presets;
|
||||||
|
const presetIds = Object.keys(presets);
|
||||||
|
|
||||||
|
// Don't delete if it's the last preset
|
||||||
|
if (presetIds.length <= 1) {
|
||||||
|
// console.warn('[RPG Companion] Cannot delete the last preset');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any character associations using this preset
|
||||||
|
const associations = extensionSettings.presetManager.characterAssociations;
|
||||||
|
for (const entityKey of Object.keys(associations)) {
|
||||||
|
if (associations[entityKey] === presetId) {
|
||||||
|
delete associations[entityKey];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the preset
|
||||||
|
delete presets[presetId];
|
||||||
|
|
||||||
|
// If the deleted preset was active, switch to the first available preset
|
||||||
|
if (extensionSettings.presetManager.activePresetId === presetId) {
|
||||||
|
const remainingIds = Object.keys(presets);
|
||||||
|
if (remainingIds.length > 0) {
|
||||||
|
loadPreset(remainingIds[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveSettings();
|
||||||
|
// console.log(`[RPG Companion] Deleted preset ${presetId}`);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associates the current preset with the current character/group
|
||||||
|
*/
|
||||||
|
export function associatePresetWithCurrentEntity() {
|
||||||
|
const entityKey = getCurrentEntityKey();
|
||||||
|
const activePresetId = extensionSettings.presetManager.activePresetId;
|
||||||
|
|
||||||
|
if (entityKey && activePresetId) {
|
||||||
|
extensionSettings.presetManager.characterAssociations[entityKey] = activePresetId;
|
||||||
|
saveSettings();
|
||||||
|
// console.log(`[RPG Companion] Associated preset ${activePresetId} with ${entityKey}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the preset association for the current character/group
|
||||||
|
*/
|
||||||
|
export function removePresetAssociationForCurrentEntity() {
|
||||||
|
const entityKey = getCurrentEntityKey();
|
||||||
|
if (entityKey && extensionSettings.presetManager.characterAssociations[entityKey]) {
|
||||||
|
delete extensionSettings.presetManager.characterAssociations[entityKey];
|
||||||
|
saveSettings();
|
||||||
|
// console.log(`[RPG Companion] Removed preset association for ${entityKey}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the preset ID associated with the current character/group
|
||||||
|
* @returns {string|null} The associated preset ID or null
|
||||||
|
*/
|
||||||
|
export function getPresetForCurrentEntity() {
|
||||||
|
const entityKey = getCurrentEntityKey();
|
||||||
|
if (entityKey) {
|
||||||
|
return extensionSettings.presetManager.characterAssociations[entityKey] || null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the current character/group has a preset association
|
||||||
|
* @returns {boolean} True if there's an association
|
||||||
|
*/
|
||||||
|
export function hasPresetAssociation() {
|
||||||
|
const entityKey = getCurrentEntityKey();
|
||||||
|
return entityKey && extensionSettings.presetManager.characterAssociations[entityKey] !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-switches to the preset associated with the current character/group
|
||||||
|
* Called when character changes. Falls back to default preset if no association.
|
||||||
|
* @returns {boolean} True if a preset was switched, false otherwise
|
||||||
|
*/
|
||||||
|
export function autoSwitchPresetForEntity() {
|
||||||
|
const associatedPresetId = getPresetForCurrentEntity();
|
||||||
|
|
||||||
|
// If there's a character-specific preset, use it
|
||||||
|
if (associatedPresetId && associatedPresetId !== extensionSettings.presetManager.activePresetId) {
|
||||||
|
// Check if the preset still exists
|
||||||
|
if (extensionSettings.presetManager.presets[associatedPresetId]) {
|
||||||
|
return loadPreset(associatedPresetId);
|
||||||
|
} else {
|
||||||
|
// Preset was deleted, remove the stale association
|
||||||
|
removePresetAssociationForCurrentEntity();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No character association - fall back to default preset if set
|
||||||
|
if (!associatedPresetId) {
|
||||||
|
const defaultPresetId = extensionSettings.presetManager.defaultPresetId;
|
||||||
|
if (defaultPresetId &&
|
||||||
|
defaultPresetId !== extensionSettings.presetManager.activePresetId &&
|
||||||
|
extensionSettings.presetManager.presets[defaultPresetId]) {
|
||||||
|
return loadPreset(defaultPresetId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports presets for sharing (without character associations)
|
||||||
|
* @param {string[]} presetIds - Array of preset IDs to export, or empty for all
|
||||||
|
* @returns {Object} Export data object
|
||||||
|
*/
|
||||||
|
export function exportPresets(presetIds = []) {
|
||||||
|
const presetsToExport = {};
|
||||||
|
const allPresets = extensionSettings.presetManager.presets;
|
||||||
|
|
||||||
|
// If no specific IDs provided, export all
|
||||||
|
const idsToExport = presetIds.length > 0 ? presetIds : Object.keys(allPresets);
|
||||||
|
|
||||||
|
for (const id of idsToExport) {
|
||||||
|
if (allPresets[id]) {
|
||||||
|
presetsToExport[id] = {
|
||||||
|
id: allPresets[id].id,
|
||||||
|
name: allPresets[id].name,
|
||||||
|
trackerConfig: allPresets[id].trackerConfig
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
version: '1.0',
|
||||||
|
exportDate: new Date().toISOString(),
|
||||||
|
presets: presetsToExport
|
||||||
|
// Note: characterAssociations are intentionally NOT exported
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Imports presets from an export file
|
||||||
|
* @param {Object} importData - The imported data object
|
||||||
|
* @param {boolean} overwrite - If true, overwrites existing presets with same name
|
||||||
|
* @returns {number} Number of presets imported
|
||||||
|
*/
|
||||||
|
export function importPresets(importData, overwrite = false) {
|
||||||
|
if (!importData.presets || typeof importData.presets !== 'object') {
|
||||||
|
throw new Error('Invalid import data: missing presets');
|
||||||
|
}
|
||||||
|
|
||||||
|
let importCount = 0;
|
||||||
|
const existingNames = new Set(
|
||||||
|
Object.values(extensionSettings.presetManager.presets).map(p => p.name.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const [originalId, preset] of Object.entries(importData.presets)) {
|
||||||
|
if (!preset.name || !preset.trackerConfig) {
|
||||||
|
continue; // Skip invalid presets
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = preset.name;
|
||||||
|
const nameLower = name.toLowerCase();
|
||||||
|
|
||||||
|
// Check for name collision
|
||||||
|
if (existingNames.has(nameLower)) {
|
||||||
|
if (overwrite) {
|
||||||
|
// Find and delete the existing preset with this name
|
||||||
|
for (const [existingId, existingPreset] of Object.entries(extensionSettings.presetManager.presets)) {
|
||||||
|
if (existingPreset.name.toLowerCase() === nameLower) {
|
||||||
|
delete extensionSettings.presetManager.presets[existingId];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Generate a unique name
|
||||||
|
let counter = 1;
|
||||||
|
while (existingNames.has(`${nameLower} (${counter})`)) {
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
name = `${preset.name} (${counter})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new preset with new ID
|
||||||
|
const newId = `preset_${Date.now()}_${importCount}`;
|
||||||
|
extensionSettings.presetManager.presets[newId] = {
|
||||||
|
id: newId,
|
||||||
|
name: name,
|
||||||
|
trackerConfig: JSON.parse(JSON.stringify(preset.trackerConfig))
|
||||||
|
};
|
||||||
|
existingNames.add(name.toLowerCase());
|
||||||
|
importCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (importCount > 0) {
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
return importCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -250,6 +250,18 @@ export let extensionSettings = {
|
|||||||
recentEvents: false // Boolean for recent events widget lock
|
recentEvents: false // Boolean for recent events widget lock
|
||||||
},
|
},
|
||||||
characters: {} // Object mapping character names to their locked fields (e.g., {"Sarah": {relationship: true, thoughts: false}})
|
characters: {} // Object mapping character names to their locked fields (e.g., {"Sarah": {relationship: true, thoughts: false}})
|
||||||
|
},
|
||||||
|
// Preset management for tracker configurations
|
||||||
|
presetManager: {
|
||||||
|
// Map of preset ID to preset data (contains name and trackerConfig)
|
||||||
|
presets: {},
|
||||||
|
// Map of character/group entity to preset ID (e.g., "char_0": "preset_123", "group_abc": "preset_456")
|
||||||
|
// Note: This is stored separately and NOT exported with presets
|
||||||
|
characterAssociations: {},
|
||||||
|
// Currently active preset ID
|
||||||
|
activePresetId: null,
|
||||||
|
// Default preset ID (used when no character association exists)
|
||||||
|
defaultPresetId: null
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+1
-3
@@ -26,15 +26,13 @@
|
|||||||
"template.settingsModal.display.panelPositionOptions.right": "Right Sidebar",
|
"template.settingsModal.display.panelPositionOptions.right": "Right Sidebar",
|
||||||
"template.settingsModal.display.panelPositionOptions.left": "Left Sidebar",
|
"template.settingsModal.display.panelPositionOptions.left": "Left Sidebar",
|
||||||
"template.settingsModal.display.toggleAutoUpdate": "Auto-update after messages",
|
"template.settingsModal.display.toggleAutoUpdate": "Auto-update after messages",
|
||||||
"template.settingsModal.display.showUserStats": "Show User Stats",
|
"template.settingsModal.display.toggleAutoUpdateNote": "Automatically refresh RPG info after each message.",
|
||||||
"template.settingsModal.display.showUserStats": "Show User Stats",
|
"template.settingsModal.display.showUserStats": "Show User Stats",
|
||||||
"template.settingsModal.display.showUserStatsNote": "Enable User Stats that track your persona's statistics, mood, attributes, skills, etc.",
|
"template.settingsModal.display.showUserStatsNote": "Enable User Stats that track your persona's statistics, mood, attributes, skills, etc.",
|
||||||
"template.settingsModal.display.showInfoBox": "Show Info Box",
|
"template.settingsModal.display.showInfoBox": "Show Info Box",
|
||||||
"template.settingsModal.display.showInfoBoxNote": "Display location, time, weather, and recent events.",
|
"template.settingsModal.display.showInfoBoxNote": "Display location, time, weather, and recent events.",
|
||||||
"template.settingsModal.display.showPresentCharacters": "Show Present Characters",
|
"template.settingsModal.display.showPresentCharacters": "Show Present Characters",
|
||||||
"template.settingsModal.display.showPresentCharactersNote": "Display character portraits with their current thoughts and status.",
|
"template.settingsModal.display.showPresentCharactersNote": "Display character portraits with their current thoughts and status.",
|
||||||
"template.settingsModal.display.toggleAutoUpdate": "Auto-update after messages",
|
|
||||||
"template.settingsModal.display.toggleAutoUpdateNote": "Automatically refresh RPG info after each message.",
|
|
||||||
"template.settingsModal.display.narratorMode": "Narrator Mode",
|
"template.settingsModal.display.narratorMode": "Narrator Mode",
|
||||||
"template.settingsModal.display.narratorModeNote": "Use character card as narrator. Infer characters from context instead of using fixed character references.",
|
"template.settingsModal.display.narratorModeNote": "Use character card as narrator. Infer characters from context instead of using fixed character references.",
|
||||||
"template.settingsModal.display.showInventory": "Show Inventory",
|
"template.settingsModal.display.showInventory": "Show Inventory",
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
updateCommittedTrackerData,
|
updateCommittedTrackerData,
|
||||||
$musicPlayerContainer
|
$musicPlayerContainer
|
||||||
} from '../../core/state.js';
|
} from '../../core/state.js';
|
||||||
import { saveChatData, loadChatData } from '../../core/persistence.js';
|
import { saveChatData, loadChatData, autoSwitchPresetForEntity } from '../../core/persistence.js';
|
||||||
import { i18n } from '../../core/i18n.js';
|
import { i18n } from '../../core/i18n.js';
|
||||||
|
|
||||||
// Generation & Parsing
|
// Generation & Parsing
|
||||||
@@ -297,6 +297,12 @@ export function onCharacterChanged() {
|
|||||||
$(window).off('resize.thoughtPanel');
|
$(window).off('resize.thoughtPanel');
|
||||||
$(document).off('click.thoughtPanel');
|
$(document).off('click.thoughtPanel');
|
||||||
|
|
||||||
|
// Auto-switch to the preset associated with this character/group (if any)
|
||||||
|
const presetSwitched = autoSwitchPresetForEntity();
|
||||||
|
// if (presetSwitched) {
|
||||||
|
// console.log('[RPG Companion] Auto-switched preset for character');
|
||||||
|
// }
|
||||||
|
|
||||||
// Load chat-specific data when switching chats
|
// Load chat-specific data when switching chats
|
||||||
loadChatData();
|
loadChatData();
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export function initPromptsEditor() {
|
|||||||
$(document).on('click', '#rpg-prompts-save', function() {
|
$(document).on('click', '#rpg-prompts-save', function() {
|
||||||
savePrompts();
|
savePrompts();
|
||||||
closePromptsEditor();
|
closePromptsEditor();
|
||||||
toastr.success('Prompts saved successfully');
|
toastr.success('Prompts saved successfully.');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Cancel button
|
// Cancel button
|
||||||
@@ -62,14 +62,14 @@ export function initPromptsEditor() {
|
|||||||
// Restore All button
|
// Restore All button
|
||||||
$(document).on('click', '#rpg-prompts-restore-all', function() {
|
$(document).on('click', '#rpg-prompts-restore-all', function() {
|
||||||
restoreAllToDefaults();
|
restoreAllToDefaults();
|
||||||
toastr.success('All prompts restored to defaults');
|
toastr.success('All prompts restored to defaults.');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Individual restore buttons
|
// Individual restore buttons
|
||||||
$(document).on('click', '.rpg-restore-prompt-btn', function() {
|
$(document).on('click', '.rpg-restore-prompt-btn', function() {
|
||||||
const promptType = $(this).data('prompt');
|
const promptType = $(this).data('prompt');
|
||||||
restorePromptToDefault(promptType);
|
restorePromptToDefault(promptType);
|
||||||
toastr.success('Prompt restored to default');
|
toastr.success('Prompt restored to default.');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Close on background click
|
// Close on background click
|
||||||
|
|||||||
+224
-18
@@ -4,7 +4,27 @@
|
|||||||
*/
|
*/
|
||||||
import { i18n } from '../../core/i18n.js';
|
import { i18n } from '../../core/i18n.js';
|
||||||
import { extensionSettings } from '../../core/state.js';
|
import { extensionSettings } from '../../core/state.js';
|
||||||
import { saveSettings } from '../../core/persistence.js';
|
import {
|
||||||
|
saveSettings,
|
||||||
|
getPresets,
|
||||||
|
getPreset,
|
||||||
|
getActivePresetId,
|
||||||
|
getDefaultPresetId,
|
||||||
|
setDefaultPreset,
|
||||||
|
isDefaultPreset,
|
||||||
|
createPreset,
|
||||||
|
saveToPreset,
|
||||||
|
loadPreset,
|
||||||
|
renamePreset,
|
||||||
|
deletePreset,
|
||||||
|
associatePresetWithCurrentEntity,
|
||||||
|
removePresetAssociationForCurrentEntity,
|
||||||
|
getPresetForCurrentEntity,
|
||||||
|
hasPresetAssociation,
|
||||||
|
getCurrentEntityName,
|
||||||
|
exportPresets,
|
||||||
|
importPresets
|
||||||
|
} from '../../core/persistence.js';
|
||||||
import { renderUserStats } from '../rendering/userStats.js';
|
import { renderUserStats } from '../rendering/userStats.js';
|
||||||
import { renderInfoBox } from '../rendering/infoBox.js';
|
import { renderInfoBox } from '../rendering/infoBox.js';
|
||||||
import { renderThoughts } from '../rendering/thoughts.js';
|
import { renderThoughts } from '../rendering/thoughts.js';
|
||||||
@@ -78,6 +98,112 @@ export function initTrackerEditor() {
|
|||||||
$(document).on('click', '#rpg-editor-import', function() {
|
$(document).on('click', '#rpg-editor-import', function() {
|
||||||
importTrackerPreset();
|
importTrackerPreset();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Preset select change
|
||||||
|
$(document).on('change', '#rpg-preset-select', function() {
|
||||||
|
const presetId = $(this).val();
|
||||||
|
if (presetId && presetId !== getActivePresetId()) {
|
||||||
|
// Save current changes to the old preset before switching
|
||||||
|
const currentPresetId = getActivePresetId();
|
||||||
|
if (currentPresetId) {
|
||||||
|
saveToPreset(currentPresetId);
|
||||||
|
}
|
||||||
|
// Load the new preset
|
||||||
|
if (loadPreset(presetId)) {
|
||||||
|
tempConfig = JSON.parse(JSON.stringify(extensionSettings.trackerConfig));
|
||||||
|
renderEditorUI();
|
||||||
|
updatePresetUI();
|
||||||
|
toastr.success(`Switched to preset "${getPreset(presetId)?.name || 'Unknown'}".`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// New preset button
|
||||||
|
$(document).on('click', '#rpg-preset-new', function() {
|
||||||
|
const name = prompt('Enter a name for the new preset:');
|
||||||
|
if (name && name.trim()) {
|
||||||
|
const newId = createPreset(name.trim());
|
||||||
|
updatePresetUI();
|
||||||
|
$('#rpg-preset-select').val(newId);
|
||||||
|
toastr.success(`Created preset "${name.trim()}".`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set as default preset button
|
||||||
|
$(document).on('click', '#rpg-preset-default', function() {
|
||||||
|
const currentPresetId = getActivePresetId();
|
||||||
|
if (currentPresetId) {
|
||||||
|
setDefaultPreset(currentPresetId);
|
||||||
|
updatePresetUI();
|
||||||
|
const preset = getPreset(currentPresetId);
|
||||||
|
toastr.success(`"${preset?.name || 'Unknown'}" is now the default preset.`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete preset button
|
||||||
|
$(document).on('click', '#rpg-preset-delete', function() {
|
||||||
|
const currentPresetId = getActivePresetId();
|
||||||
|
const presets = getPresets();
|
||||||
|
if (Object.keys(presets).length <= 1) {
|
||||||
|
toastr.warning('Cannot delete the last preset.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const preset = getPreset(currentPresetId);
|
||||||
|
if (confirm(`Are you sure you want to delete the preset "${preset?.name || 'Unknown'}"?`)) {
|
||||||
|
if (deletePreset(currentPresetId)) {
|
||||||
|
tempConfig = JSON.parse(JSON.stringify(extensionSettings.trackerConfig));
|
||||||
|
renderEditorUI();
|
||||||
|
updatePresetUI();
|
||||||
|
toastr.success('Preset deleted.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Associate preset checkbox
|
||||||
|
$(document).on('change', '#rpg-preset-associate', function() {
|
||||||
|
if ($(this).is(':checked')) {
|
||||||
|
associatePresetWithCurrentEntity();
|
||||||
|
toastr.info(`This preset will be used for ${getCurrentEntityName()}.`);
|
||||||
|
} else {
|
||||||
|
removePresetAssociationForCurrentEntity();
|
||||||
|
toastr.info(`Preset association removed for ${getCurrentEntityName()}.`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the preset management UI (dropdown, association checkbox, entity name)
|
||||||
|
*/
|
||||||
|
function updatePresetUI() {
|
||||||
|
const presets = getPresets();
|
||||||
|
const activePresetId = getActivePresetId();
|
||||||
|
const defaultPresetId = getDefaultPresetId();
|
||||||
|
const $select = $('#rpg-preset-select');
|
||||||
|
|
||||||
|
// Populate the dropdown
|
||||||
|
$select.empty();
|
||||||
|
for (const [id, preset] of Object.entries(presets)) {
|
||||||
|
const isDefault = id === defaultPresetId;
|
||||||
|
const starPrefix = isDefault ? '★ ' : '';
|
||||||
|
$select.append(`<option value="${id}">${starPrefix}${preset.name}</option>`);
|
||||||
|
}
|
||||||
|
$select.val(activePresetId);
|
||||||
|
|
||||||
|
// Update the default button appearance
|
||||||
|
const $defaultBtn = $('#rpg-preset-default');
|
||||||
|
if (isDefaultPreset(activePresetId)) {
|
||||||
|
$defaultBtn.addClass('rpg-btn-active').attr('title', 'This is the default preset');
|
||||||
|
} else {
|
||||||
|
$defaultBtn.removeClass('rpg-btn-active').attr('title', 'Set as Default Preset');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the entity name display
|
||||||
|
const entityName = getCurrentEntityName();
|
||||||
|
$('#rpg-preset-entity-name').text(entityName);
|
||||||
|
|
||||||
|
// Update the association checkbox
|
||||||
|
const isAssociated = hasPresetAssociation();
|
||||||
|
$('#rpg-preset-associate').prop('checked', isAssociated);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -91,6 +217,9 @@ function openTrackerEditor() {
|
|||||||
const theme = extensionSettings.theme || 'modern';
|
const theme = extensionSettings.theme || 'modern';
|
||||||
$editorModal.attr('data-theme', theme);
|
$editorModal.attr('data-theme', theme);
|
||||||
|
|
||||||
|
// Update preset UI
|
||||||
|
updatePresetUI();
|
||||||
|
|
||||||
renderEditorUI();
|
renderEditorUI();
|
||||||
$editorModal.addClass('is-open').css('display', '');
|
$editorModal.addClass('is-open').css('display', '');
|
||||||
}
|
}
|
||||||
@@ -116,7 +245,14 @@ function closeTrackerEditor() {
|
|||||||
*/
|
*/
|
||||||
function applyTrackerConfig() {
|
function applyTrackerConfig() {
|
||||||
tempConfig = null; // Clear temp config
|
tempConfig = null; // Clear temp config
|
||||||
saveSettings();
|
|
||||||
|
// Save to the current preset
|
||||||
|
const currentPresetId = getActivePresetId();
|
||||||
|
if (currentPresetId) {
|
||||||
|
saveToPreset(currentPresetId);
|
||||||
|
} else {
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
// Re-render all trackers with new config
|
// Re-render all trackers with new config
|
||||||
renderUserStats();
|
renderUserStats();
|
||||||
@@ -323,22 +459,8 @@ function importTrackerPreset() {
|
|||||||
// Migrate old preset format to current format
|
// Migrate old preset format to current format
|
||||||
const migratedConfig = migrateTrackerPreset(data.trackerConfig);
|
const migratedConfig = migrateTrackerPreset(data.trackerConfig);
|
||||||
|
|
||||||
// Ask for confirmation
|
// Show import mode selection dialog
|
||||||
const confirmMessage = i18n.getTranslation('template.trackerEditorModal.messages.importConfirm') ||
|
showImportModeDialog(migratedConfig, data.name || file.name.replace('.json', ''));
|
||||||
'This will replace your current tracker configuration. Continue?';
|
|
||||||
|
|
||||||
if (!confirm(confirmMessage)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply the migrated configuration
|
|
||||||
extensionSettings.trackerConfig = migratedConfig;
|
|
||||||
|
|
||||||
// Re-render the editor UI
|
|
||||||
renderEditorUI();
|
|
||||||
|
|
||||||
// console.log('[RPG Companion] 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);
|
||||||
toastr.error(i18n.getTranslation('template.trackerEditorModal.messages.importError') ||
|
toastr.error(i18n.getTranslation('template.trackerEditorModal.messages.importError') ||
|
||||||
@@ -350,6 +472,90 @@ function importTrackerPreset() {
|
|||||||
input.click();
|
input.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show dialog to choose import mode
|
||||||
|
* @param {Object} migratedConfig - The migrated tracker config
|
||||||
|
* @param {string} suggestedName - Suggested name for new preset
|
||||||
|
*/
|
||||||
|
function showImportModeDialog(migratedConfig, suggestedName) {
|
||||||
|
// Create dialog overlay
|
||||||
|
const dialogHtml = `
|
||||||
|
<div id="rpg-import-mode-dialog" class="rpg-import-dialog-overlay">
|
||||||
|
<div class="rpg-import-dialog">
|
||||||
|
<h4><i class="fa-solid fa-file-import"></i> Import Configuration</h4>
|
||||||
|
<p>How would you like to import this configuration?</p>
|
||||||
|
<div class="rpg-import-dialog-buttons">
|
||||||
|
<button id="rpg-import-to-current" class="rpg-btn-secondary">
|
||||||
|
<i class="fa-solid fa-arrow-right-to-bracket"></i>
|
||||||
|
Apply to Current Preset
|
||||||
|
</button>
|
||||||
|
<button id="rpg-import-as-new" class="rpg-btn-primary">
|
||||||
|
<i class="fa-solid fa-plus"></i>
|
||||||
|
Create New Preset
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button id="rpg-import-cancel" class="rpg-btn-cancel">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
$('body').append(dialogHtml);
|
||||||
|
const $dialog = $('#rpg-import-mode-dialog');
|
||||||
|
|
||||||
|
// Import to current preset
|
||||||
|
$('#rpg-import-to-current').on('click', () => {
|
||||||
|
$dialog.remove();
|
||||||
|
|
||||||
|
// Apply the migrated configuration to current
|
||||||
|
extensionSettings.trackerConfig = migratedConfig;
|
||||||
|
|
||||||
|
// Save to the active preset (saveToPreset uses current trackerConfig)
|
||||||
|
const activePresetId = getActivePresetId();
|
||||||
|
if (activePresetId) {
|
||||||
|
saveToPreset(activePresetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-render the editor UI
|
||||||
|
renderEditorUI();
|
||||||
|
|
||||||
|
toastr.success('Configuration applied to current preset.');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Import as new preset
|
||||||
|
$('#rpg-import-as-new').on('click', () => {
|
||||||
|
$dialog.remove();
|
||||||
|
|
||||||
|
// Prompt for preset name
|
||||||
|
const presetName = prompt('Enter a name for the new preset:', suggestedName);
|
||||||
|
if (!presetName) return;
|
||||||
|
|
||||||
|
// Set the migrated config as current first
|
||||||
|
extensionSettings.trackerConfig = migratedConfig;
|
||||||
|
|
||||||
|
// Create new preset (createPreset uses current trackerConfig)
|
||||||
|
const newPresetId = createPreset(presetName);
|
||||||
|
if (newPresetId) {
|
||||||
|
// Load the new preset
|
||||||
|
loadPreset(newPresetId);
|
||||||
|
renderEditorUI();
|
||||||
|
updatePresetUI();
|
||||||
|
toastr.success(`Created new preset: ${presetName}.`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cancel
|
||||||
|
$('#rpg-import-cancel').on('click', () => {
|
||||||
|
$dialog.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close on overlay click
|
||||||
|
$dialog.on('click', (e) => {
|
||||||
|
if (e.target === $dialog[0]) {
|
||||||
|
$dialog.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the editor UI based on current config
|
* Render the editor UI based on current config
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -3933,6 +3933,70 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
|||||||
TRACKER EDITOR MODAL
|
TRACKER EDITOR MODAL
|
||||||
============================================ */
|
============================================ */
|
||||||
|
|
||||||
|
/* Preset Management Section */
|
||||||
|
.rpg-preset-management {
|
||||||
|
padding: 0.75em 1em;
|
||||||
|
background: var(--rpg-accent);
|
||||||
|
border-bottom: 2px solid var(--rpg-border);
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-preset-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5em;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-preset-row label {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.9em;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-preset-row .rpg-select {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 120px;
|
||||||
|
max-width: 250px;
|
||||||
|
height: 2.2em;
|
||||||
|
padding: 0 0.6em;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-preset-row .rpg-btn-icon {
|
||||||
|
height: 2.2em;
|
||||||
|
width: 2.2em;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 0.9em;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-preset-row .rpg-btn-icon.rpg-btn-active {
|
||||||
|
background: var(--rpg-highlight);
|
||||||
|
border-color: var(--rpg-highlight);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-preset-association-row {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
padding-top: 0.5em;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-preset-association-row .checkbox_label {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.85em;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-preset-association-row .checkbox_label strong {
|
||||||
|
color: var(--rpg-highlight);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
/* Editor tabs */
|
/* Editor tabs */
|
||||||
.rpg-editor-tabs {
|
.rpg-editor-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -9693,3 +9757,103 @@ body[data-theme="cyberpunk"] .rpg-music-widget-play {
|
|||||||
#send_form {
|
#send_form {
|
||||||
position: relative !important;
|
position: relative !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================
|
||||||
|
IMPORT MODE DIALOG STYLES
|
||||||
|
============================================ */
|
||||||
|
|
||||||
|
/* Import Mode Dialog Overlay */
|
||||||
|
.rpg-import-dialog-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 100000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-import-dialog {
|
||||||
|
background: var(--SmartThemeBlurTintColor, #2a2a3e);
|
||||||
|
border: 1px solid var(--SmartThemeBorderColor, #4a4a4a);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 400px;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-import-dialog h4 {
|
||||||
|
margin: 0 0 15px 0;
|
||||||
|
color: var(--SmartThemeBodyColor, #eee);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-import-dialog h4 i {
|
||||||
|
margin-right: 8px;
|
||||||
|
color: var(--SmartThemeQuoteColor, #e94560);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-import-dialog p {
|
||||||
|
margin: 0 0 20px 0;
|
||||||
|
color: var(--SmartThemeBodyColor, #ccc);
|
||||||
|
font-size: 0.95em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-import-dialog-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-import-dialog-buttons button {
|
||||||
|
padding: 10px 15px;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.95em;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-import-dialog .rpg-btn-secondary {
|
||||||
|
background: var(--black30a, rgba(0, 0, 0, 0.3));
|
||||||
|
border: 1px solid var(--SmartThemeBorderColor, #4a4a4a);
|
||||||
|
color: var(--SmartThemeBodyColor, #ccc);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-import-dialog .rpg-btn-secondary:hover {
|
||||||
|
background: var(--SmartThemeBorderColor, #4a4a4a);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-import-dialog .rpg-btn-primary {
|
||||||
|
background: var(--SmartThemeQuoteColor, #e94560);
|
||||||
|
border: 1px solid var(--SmartThemeQuoteColor, #e94560);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-import-dialog .rpg-btn-primary:hover {
|
||||||
|
background: var(--SmartThemeQuoteColor, #ff5a75);
|
||||||
|
border-color: var(--SmartThemeQuoteColor, #ff5a75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-import-dialog .rpg-btn-cancel {
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--SmartThemeBodyColor, #888);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.85em;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rpg-import-dialog .rpg-btn-cancel:hover {
|
||||||
|
color: var(--SmartThemeBodyColor, #ccc);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|||||||
@@ -670,6 +670,31 @@
|
|||||||
aria-label="Close tracker editor">×</button>
|
aria-label="Close tracker editor">×</button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<!-- Preset Management Section -->
|
||||||
|
<div class="rpg-preset-management">
|
||||||
|
<div class="rpg-preset-row">
|
||||||
|
<label for="rpg-preset-select">Preset:</label>
|
||||||
|
<select id="rpg-preset-select" class="rpg-select">
|
||||||
|
<!-- Options populated by JavaScript -->
|
||||||
|
</select>
|
||||||
|
<button id="rpg-preset-new" class="rpg-btn-icon" type="button" title="Create New Preset">
|
||||||
|
<i class="fa-solid fa-plus"></i>
|
||||||
|
</button>
|
||||||
|
<button id="rpg-preset-default" class="rpg-btn-icon" type="button" title="Set as Default Preset">
|
||||||
|
<i class="fa-solid fa-star"></i>
|
||||||
|
</button>
|
||||||
|
<button id="rpg-preset-delete" class="rpg-btn-icon" type="button" title="Delete Current Preset">
|
||||||
|
<i class="fa-solid fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="rpg-preset-association-row">
|
||||||
|
<label class="checkbox_label">
|
||||||
|
<input type="checkbox" id="rpg-preset-associate">
|
||||||
|
<span>Use this preset for: <strong id="rpg-preset-entity-name">Character</strong></span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Tabs -->
|
<!-- Tabs -->
|
||||||
<div class="rpg-editor-tabs">
|
<div class="rpg-editor-tabs">
|
||||||
<button class="rpg-editor-tab active" data-tab="userStats">
|
<button class="rpg-editor-tab active" data-tab="userStats">
|
||||||
|
|||||||
Reference in New Issue
Block a user