diff --git a/src/core/persistence.js b/src/core/persistence.js index a495e26..8cfc9d1 100644 --- a/src/core/persistence.js +++ b/src/core/persistence.js @@ -126,6 +126,9 @@ export function loadSettings() { migrateToTrackerConfig(); saveSettings(); // Persist migration } + + // Migrate to preset manager system if presets don't exist + migrateToPresetManager(); } catch (error) { console.error('[RPG Companion] Error loading settings:', error); 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; +} + diff --git a/src/core/state.js b/src/core/state.js index 7ba229c..71f2da9 100644 --- a/src/core/state.js +++ b/src/core/state.js @@ -250,6 +250,18 @@ export let extensionSettings = { recentEvents: false // Boolean for recent events widget lock }, 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 } }; diff --git a/src/systems/integration/sillytavern.js b/src/systems/integration/sillytavern.js index c71c3c2..4b534a6 100644 --- a/src/systems/integration/sillytavern.js +++ b/src/systems/integration/sillytavern.js @@ -22,7 +22,7 @@ import { updateCommittedTrackerData, $musicPlayerContainer } 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'; // Generation & Parsing @@ -297,6 +297,12 @@ export function onCharacterChanged() { $(window).off('resize.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 loadChatData(); diff --git a/src/systems/ui/trackerEditor.js b/src/systems/ui/trackerEditor.js index 9ea4b32..1c39fd6 100644 --- a/src/systems/ui/trackerEditor.js +++ b/src/systems/ui/trackerEditor.js @@ -4,7 +4,27 @@ */ import { i18n } from '../../core/i18n.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 { renderInfoBox } from '../rendering/infoBox.js'; import { renderThoughts } from '../rendering/thoughts.js'; @@ -78,6 +98,112 @@ export function initTrackerEditor() { $(document).on('click', '#rpg-editor-import', function() { 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(``); + } + $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'; $editorModal.attr('data-theme', theme); + // Update preset UI + updatePresetUI(); + renderEditorUI(); $editorModal.addClass('is-open').css('display', ''); } @@ -116,7 +245,14 @@ function closeTrackerEditor() { */ function applyTrackerConfig() { 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 renderUserStats(); @@ -323,22 +459,8 @@ function importTrackerPreset() { // Migrate old preset format to current format const migratedConfig = migrateTrackerPreset(data.trackerConfig); - // Ask for confirmation - const confirmMessage = i18n.getTranslation('template.trackerEditorModal.messages.importConfirm') || - '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!'); + // Show import mode selection dialog + showImportModeDialog(migratedConfig, data.name || file.name.replace('.json', '')); } catch (error) { console.error('[RPG Companion] Error importing tracker preset:', error); toastr.error(i18n.getTranslation('template.trackerEditorModal.messages.importError') || @@ -350,6 +472,90 @@ function importTrackerPreset() { 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 = ` +
+ `; + + $('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 */ diff --git a/style.css b/style.css index 92d93a8..423e960 100644 --- a/style.css +++ b/style.css @@ -3933,6 +3933,70 @@ body:has(.rpg-panel.rpg-position-left) #sheld { 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 */ .rpg-editor-tabs { display: flex; @@ -9693,3 +9757,103 @@ body[data-theme="cyberpunk"] .rpg-music-widget-play { #send_form { 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; +} diff --git a/template.html b/template.html index c01e87e..0af0e58 100644 --- a/template.html +++ b/template.html @@ -670,6 +670,31 @@ aria-label="Close tracker editor">× + +