/** * Tracker Editor Module * Provides UI for customizing tracker configurations */ import { i18n } from '../../core/i18n.js'; import { extensionSettings } from '../../core/state.js'; import { saveSettings } from '../../core/persistence.js'; import { renderUserStats } from '../rendering/userStats.js'; import { renderInfoBox } from '../rendering/infoBox.js'; import { renderThoughts } from '../rendering/thoughts.js'; import { renderSkills } from '../rendering/skills.js'; let $editorModal = null; let activeTab = 'userStats'; let tempConfig = null; // Temporary config for cancel functionality /** * Initialize the tracker editor modal */ export function initTrackerEditor() { // Modal will be in template.html, just set up event listeners $editorModal = $('#rpg-tracker-editor-popup'); if (!$editorModal.length) { console.error('[RPG Companion] Tracker editor modal not found in template'); return; } // Tab switching $(document).on('click', '.rpg-editor-tab', function() { $('.rpg-editor-tab').removeClass('active'); $(this).addClass('active'); activeTab = $(this).data('tab'); $('.rpg-editor-tab-content').hide(); $(`#rpg-editor-tab-${activeTab}`).show(); }); // Save button $(document).on('click', '#rpg-editor-save', function() { applyTrackerConfig(); closeTrackerEditor(); }); // Cancel button $(document).on('click', '#rpg-editor-cancel', function() { closeTrackerEditor(); }); // Close X button $(document).on('click', '#rpg-close-tracker-editor', function() { closeTrackerEditor(); }); // Reset button $(document).on('click', '#rpg-editor-reset', function() { resetToDefaults(); renderEditorUI(); }); // Close on background click $(document).on('click', '#rpg-tracker-editor-popup', function(e) { if (e.target.id === 'rpg-tracker-editor-popup') { closeTrackerEditor(); } }); // Open button $(document).on('click', '#rpg-open-tracker-editor', function() { openTrackerEditor(); }); } /** * Open the tracker editor modal */ function openTrackerEditor() { // Create temporary copy for cancel functionality tempConfig = JSON.parse(JSON.stringify(extensionSettings.trackerConfig)); // Set theme to match current extension theme const theme = extensionSettings.theme || 'modern'; $editorModal.attr('data-theme', theme); renderEditorUI(); $editorModal.addClass('is-open').css('display', ''); } /** * Close the tracker editor modal */ function closeTrackerEditor() { // Restore from temp if canceling if (tempConfig) { extensionSettings.trackerConfig = tempConfig; tempConfig = null; } $editorModal.removeClass('is-open').addClass('is-closing'); setTimeout(() => { $editorModal.removeClass('is-closing').hide(); }, 200); } /** * Apply the tracker configuration and refresh all trackers */ function applyTrackerConfig() { tempConfig = null; // Clear temp config saveSettings(); // Re-render all trackers with new config renderUserStats(); renderInfoBox(); renderThoughts(); } /** * Reset configuration to defaults */ function resetToDefaults() { extensionSettings.trackerConfig = { userStats: { customStats: [ { id: 'health', name: 'Health', enabled: true }, { id: 'satiety', name: 'Satiety', enabled: true }, { id: 'energy', name: 'Energy', enabled: true }, { id: 'hygiene', name: 'Hygiene', enabled: true }, { id: 'arousal', name: 'Arousal', enabled: true } ], showRPGAttributes: true, rpgAttributes: [ { id: 'str', name: 'STR', description: '', enabled: true }, { id: 'dex', name: 'DEX', description: '', enabled: true }, { id: 'con', name: 'CON', description: '', enabled: true }, { id: 'int', name: 'INT', description: '', enabled: true }, { id: 'wis', name: 'WIS', description: '', enabled: true }, { id: 'cha', name: 'CHA', description: '', enabled: true } ], statusSection: { enabled: true, showMoodEmoji: true, customFields: ['Conditions'] }, skillsSection: { enabled: false, label: 'Skills', customFields: [] } }, infoBox: { widgets: { date: { enabled: true, format: 'Weekday, Month, Year' }, weather: { enabled: true }, temperature: { enabled: true, unit: 'C' }, time: { enabled: true }, location: { enabled: true }, recentEvents: { enabled: true } } }, presentCharacters: { showEmoji: true, showName: true, relationshipFields: ['Lover', 'Friend', 'Ally', 'Enemy', 'Neutral'], relationshipEmojis: { 'Lover': '❤️', 'Friend': '⭐', 'Ally': '🤝', 'Enemy': '⚔️', 'Neutral': '⚖️' }, customFields: [ { id: 'appearance', name: 'Appearance', enabled: true, description: 'Visible physical appearance (clothing, hair, notable features)' }, { id: 'demeanor', name: 'Demeanor', enabled: true, description: 'Observable demeanor or emotional state' } ], thoughts: { enabled: true, name: 'Thoughts', description: 'Internal monologue (in first person POV, up to three sentences long)' }, characterStats: { enabled: false, customStats: [ { id: 'health', name: 'Health', enabled: true, colorLow: '#ff4444', colorHigh: '#44ff44' }, { id: 'energy', name: 'Energy', enabled: true, colorLow: '#ffaa00', colorHigh: '#44ffff' } ] } } }; } /** * Render the editor UI based on current config */ function renderEditorUI() { renderUserStatsTab(); renderInfoBoxTab(); renderPresentCharactersTab(); } /** * Render User Stats configuration tab */ function renderUserStatsTab() { const config = extensionSettings.trackerConfig.userStats; let html = '
'; // Custom Stats section html += `

${i18n.getTranslation('template.trackerEditorModal.userStatsTab.customStatsTitle')}

`; html += '
'; config.customStats.forEach((stat, index) => { const statDesc = stat.description || ''; html += `
`; }); html += '
'; html += ``; // RPG Attributes section html += `

${i18n.getTranslation('template.trackerEditorModal.userStatsTab.rpgAttributesTitle')}

`; // Enable/disable toggle for entire RPG Attributes section const showRPGAttributes = config.showRPGAttributes !== undefined ? config.showRPGAttributes : true; html += '
'; html += ``; html += ``; html += '
'; // Always send attributes toggle const alwaysSendAttributes = config.alwaysSendAttributes !== undefined ? config.alwaysSendAttributes : false; html += '
'; html += ``; html += ``; html += '
'; html += `${i18n.getTranslation('template.trackerEditorModal.userStatsTab.alwaysIncludeAttributesNote')}`; html += '
'; // Ensure rpgAttributes exists in the actual config (not just local fallback) if (!config.rpgAttributes || config.rpgAttributes.length === 0) { config.rpgAttributes = [ { id: 'str', name: 'STR', description: '', enabled: true }, { id: 'dex', name: 'DEX', description: '', enabled: true }, { id: 'con', name: 'CON', description: '', enabled: true }, { id: 'int', name: 'INT', description: '', enabled: true }, { id: 'wis', name: 'WIS', description: '', enabled: true }, { id: 'cha', name: 'CHA', description: '', enabled: true } ]; // Save the defaults back to the actual config extensionSettings.trackerConfig.userStats.rpgAttributes = config.rpgAttributes; } const rpgAttributes = config.rpgAttributes; rpgAttributes.forEach((attr, index) => { const attrDesc = attr.description || ''; html += `
`; }); html += '
'; html += ``; // Status Section html += `

${i18n.getTranslation('template.trackerEditorModal.userStatsTab.statusSectionTitle')}

`; html += '
'; html += ``; html += ``; html += '
'; html += '
'; html += ``; html += ``; html += '
'; html += ``; html += ``; // Skills Section html += `

${i18n.getTranslation('template.trackerEditorModal.userStatsTab.skillsSectionTitle')}

`; // Check if skills are shown as separate section - if so, disable the toggle const skillsInSeparateTab = extensionSettings.showSkills; const skillsToggleDisabled = skillsInSeparateTab ? 'disabled' : ''; const skillsToggleStyle = skillsInSeparateTab ? 'style="opacity: 0.5; cursor: not-allowed;"' : ''; html += `
`; html += ``; html += ``; html += '
'; // Show note when skills are in separate tab if (skillsInSeparateTab) { html += `${i18n.getTranslation('template.trackerEditorModal.userStatsTab.skillsInSeparateTabNote')}`; } html += ``; html += ``; html += ``; html += '
'; // Handle both old format (string array) and new format (object array) const skillFields = config.skillsSection.customFields || []; skillFields.forEach((skill, index) => { // Support both old format (string) and new format (object) const skillName = typeof skill === 'string' ? skill : (skill.name || ''); const skillDesc = typeof skill === 'string' ? '' : (skill.description || ''); const skillEnabled = typeof skill === 'string' ? true : (skill.enabled !== false); html += `
`; }); html += '
'; html += ``; html += '
'; $('#rpg-editor-tab-userStats').html(html); setupUserStatsListeners(); } /** * Set up event listeners for User Stats tab */ function setupUserStatsListeners() { // Add stat $('#rpg-add-stat').off('click').on('click', function() { const newId = 'custom_' + Date.now(); extensionSettings.trackerConfig.userStats.customStats.push({ id: newId, name: 'New Stat', description: '', enabled: true }); // Initialize value if doesn't exist if (extensionSettings.userStats[newId] === undefined) { extensionSettings.userStats[newId] = 100; } renderUserStatsTab(); }); // Remove stat $('.rpg-stat-remove').off('click').on('click', function() { const index = $(this).data('index'); extensionSettings.trackerConfig.userStats.customStats.splice(index, 1); renderUserStatsTab(); }); // Toggle stat $('.rpg-stat-toggle').off('change').on('change', function() { const index = $(this).data('index'); extensionSettings.trackerConfig.userStats.customStats[index].enabled = $(this).is(':checked'); }); // Rename stat $('.rpg-stat-name').off('blur').on('blur', function() { const index = $(this).data('index'); extensionSettings.trackerConfig.userStats.customStats[index].name = $(this).val(); }); // Update stat description $('.rpg-stat-desc').off('blur').on('blur', function() { const index = $(this).data('index'); extensionSettings.trackerConfig.userStats.customStats[index].description = $(this).val(); }); // Add attribute $('#rpg-add-attr').off('click').on('click', function() { // Ensure rpgAttributes array exists with defaults if needed if (!extensionSettings.trackerConfig.userStats.rpgAttributes || extensionSettings.trackerConfig.userStats.rpgAttributes.length === 0) { extensionSettings.trackerConfig.userStats.rpgAttributes = [ { id: 'str', name: 'STR', description: '', enabled: true }, { id: 'dex', name: 'DEX', description: '', enabled: true }, { id: 'con', name: 'CON', description: '', enabled: true }, { id: 'int', name: 'INT', description: '', enabled: true }, { id: 'wis', name: 'WIS', description: '', enabled: true }, { id: 'cha', name: 'CHA', description: '', enabled: true } ]; } const newId = 'attr_' + Date.now(); extensionSettings.trackerConfig.userStats.rpgAttributes.push({ id: newId, name: 'NEW', description: '', enabled: true }); // Initialize value in classicStats if doesn't exist if (extensionSettings.classicStats[newId] === undefined) { extensionSettings.classicStats[newId] = 10; } renderUserStatsTab(); }); // Remove attribute $('.rpg-attr-remove').off('click').on('click', function() { const index = $(this).data('index'); extensionSettings.trackerConfig.userStats.rpgAttributes.splice(index, 1); renderUserStatsTab(); }); // Toggle attribute $('.rpg-attr-toggle').off('change').on('change', function() { const index = $(this).data('index'); extensionSettings.trackerConfig.userStats.rpgAttributes[index].enabled = $(this).is(':checked'); }); // Rename attribute $('.rpg-attr-name').off('blur').on('blur', function() { const index = $(this).data('index'); extensionSettings.trackerConfig.userStats.rpgAttributes[index].name = $(this).val(); }); // Update attribute description $('.rpg-attr-desc').off('blur').on('blur', function() { const index = $(this).data('index'); extensionSettings.trackerConfig.userStats.rpgAttributes[index].description = $(this).val(); }); // Enable/disable RPG Attributes section toggle $('#rpg-show-rpg-attrs').off('change').on('change', function() { extensionSettings.trackerConfig.userStats.showRPGAttributes = $(this).is(':checked'); }); // Always send attributes toggle $('#rpg-always-send-attrs').off('change').on('change', function() { extensionSettings.trackerConfig.userStats.alwaysSendAttributes = $(this).is(':checked'); }); // Status section toggles $('#rpg-status-enabled').off('change').on('change', function() { extensionSettings.trackerConfig.userStats.statusSection.enabled = $(this).is(':checked'); }); $('#rpg-mood-emoji').off('change').on('change', function() { extensionSettings.trackerConfig.userStats.statusSection.showMoodEmoji = $(this).is(':checked'); }); $('#rpg-status-fields').off('blur').on('blur', function() { const fields = $(this).val().split(',').map(f => f.trim()).filter(f => f); extensionSettings.trackerConfig.userStats.statusSection.customFields = fields; }); // Skills section toggles $('#rpg-skills-enabled').off('change').on('change', function() { extensionSettings.trackerConfig.userStats.skillsSection.enabled = $(this).is(':checked'); saveSettings(); // Re-render both user stats (if skills shown there) and skills section renderUserStats(); renderSkills(); }); $('#rpg-skills-label').off('blur').on('blur', function() { const newLabel = $(this).val(); extensionSettings.trackerConfig.userStats.skillsSection.label = newLabel; saveSettings(); renderUserStats(); renderSkills(); // Update the skills tab button text if it exists $('.rpg-tab-btn[data-tab="skills"] span').text(newLabel); }); // Add skill category $('#rpg-add-skill').off('click').on('click', function() { if (!extensionSettings.trackerConfig.userStats.skillsSection.customFields) { extensionSettings.trackerConfig.userStats.skillsSection.customFields = []; } extensionSettings.trackerConfig.userStats.skillsSection.customFields.push({ id: 'skill_' + Date.now(), name: 'New Skill', description: '', enabled: true }); renderUserStatsTab(); saveSettings(); renderSkills(); }); // Remove skill category $('.rpg-skill-remove').off('click').on('click', function() { const index = $(this).data('index'); extensionSettings.trackerConfig.userStats.skillsSection.customFields.splice(index, 1); renderUserStatsTab(); saveSettings(); renderSkills(); }); // Toggle skill category $('.rpg-skill-toggle').off('change').on('change', function() { const index = $(this).data('index'); ensureSkillIsObject(index); extensionSettings.trackerConfig.userStats.skillsSection.customFields[index].enabled = $(this).is(':checked'); saveSettings(); renderSkills(); }); // Rename skill category $('.rpg-skill-name').off('blur').on('blur', function() { const index = $(this).data('index'); ensureSkillIsObject(index); extensionSettings.trackerConfig.userStats.skillsSection.customFields[index].name = $(this).val(); saveSettings(); renderSkills(); }); // Update skill description $('.rpg-skill-desc').off('blur').on('blur', function() { const index = $(this).data('index'); ensureSkillIsObject(index); extensionSettings.trackerConfig.userStats.skillsSection.customFields[index].description = $(this).val(); saveSettings(); }); } /** * Helper to convert old string-format skill to object format */ function ensureSkillIsObject(index) { const skill = extensionSettings.trackerConfig.userStats.skillsSection.customFields[index]; if (typeof skill === 'string') { extensionSettings.trackerConfig.userStats.skillsSection.customFields[index] = { id: 'skill_' + Date.now(), name: skill, description: '', enabled: true }; } } /** * Render Info Box configuration tab */ function renderInfoBoxTab() { const config = extensionSettings.trackerConfig.infoBox; let html = '
'; html += `

${i18n.getTranslation('template.trackerEditorModal.infoBoxTab.widgetsTitle')}

`; // Date widget html += '
'; html += ``; html += ``; html += ''; html += '
'; // Weather widget html += '
'; html += ``; html += ``; html += '
'; // Temperature widget html += '
'; html += ``; html += ``; html += '
'; html += ``; html += ``; html += '
'; html += '
'; // Time widget html += '
'; html += ``; html += ``; html += '
'; // Location widget html += '
'; html += ``; html += ``; html += '
'; // Recent Events widget html += '
'; html += ``; html += ``; html += '
'; html += '
'; $('#rpg-editor-tab-infoBox').html(html); setupInfoBoxListeners(); } /** * Set up event listeners for Info Box tab */ function setupInfoBoxListeners() { const widgets = extensionSettings.trackerConfig.infoBox.widgets; $('#rpg-widget-date').off('change').on('change', function() { widgets.date.enabled = $(this).is(':checked'); }); $('#rpg-date-format').off('change').on('change', function() { widgets.date.format = $(this).val(); }); $('#rpg-widget-weather').off('change').on('change', function() { widgets.weather.enabled = $(this).is(':checked'); }); $('#rpg-widget-temperature').off('change').on('change', function() { widgets.temperature.enabled = $(this).is(':checked'); }); $('input[name="temp-unit"]').off('change').on('change', function() { widgets.temperature.unit = $(this).val(); }); $('#rpg-widget-time').off('change').on('change', function() { widgets.time.enabled = $(this).is(':checked'); }); $('#rpg-widget-location').off('change').on('change', function() { widgets.location.enabled = $(this).is(':checked'); }); $('#rpg-widget-events').off('change').on('change', function() { widgets.recentEvents.enabled = $(this).is(':checked'); }); } /** * Render Present Characters configuration tab */ function renderPresentCharactersTab() { const config = extensionSettings.trackerConfig.presentCharacters; let html = '
'; // Relationship Fields Section html += `

${i18n.getTranslation('template.trackerEditorModal.presentCharactersTab.relationshipStatusTitle')}

`; html += `

${i18n.getTranslation('template.trackerEditorModal.presentCharactersTab.relationshipStatusHint')}

`; html += '
'; // Show existing relationships as field → emoji pairs const relationshipEmojis = config.relationshipEmojis || { 'Lover': '❤️', 'Friend': '⭐', 'Ally': '🤝', 'Enemy': '⚔️', 'Neutral': '⚖️' }; for (const [relationship, emoji] of Object.entries(relationshipEmojis)) { html += `
`; } html += '
'; html += ``; // Custom Fields Section html += `

${i18n.getTranslation('template.trackerEditorModal.presentCharactersTab.appearanceDemeanorTitle')}

`; html += `

${i18n.getTranslation('template.trackerEditorModal.presentCharactersTab.appearanceDemeanorHint')}

`; html += '
'; config.customFields.forEach((field, index) => { html += `
`; }); html += '
'; html += ``; // Thoughts Section html += `

${i18n.getTranslation('template.trackerEditorModal.presentCharactersTab.thoughtsConfigTitle')}

`; html += '
'; html += ``; html += ``; html += '
'; html += '
'; html += '
'; html += ``; html += ``; html += '
'; html += '
'; html += ``; html += ``; html += '
'; html += '
'; // Character Stats html += `

${i18n.getTranslation('template.trackerEditorModal.presentCharactersTab.characterStatsTitle')}

`; html += '
'; html += ``; html += ``; html += '
'; html += `

${i18n.getTranslation('template.trackerEditorModal.presentCharactersTab.characterStatsHint')}

`; html += '
'; const charStats = config.characterStats?.customStats || []; charStats.forEach((stat, index) => { html += `
`; }); html += '
'; html += ``; html += '
'; $('#rpg-editor-tab-presentCharacters').html(html); setupPresentCharactersListeners(); } /** * Set up event listeners for Present Characters tab */ function setupPresentCharactersListeners() { // Add new relationship $('#rpg-add-relationship').off('click').on('click', function() { if (!extensionSettings.trackerConfig.presentCharacters.relationshipEmojis) { extensionSettings.trackerConfig.presentCharacters.relationshipEmojis = {}; } // Generate unique name to avoid overwriting existing "New Relationship" entries let newName = 'New Relationship'; let counter = 1; while (extensionSettings.trackerConfig.presentCharacters.relationshipEmojis[newName]) { newName = `New Relationship ${counter}`; counter++; } extensionSettings.trackerConfig.presentCharacters.relationshipEmojis[newName] = '😊'; // Sync relationshipFields extensionSettings.trackerConfig.presentCharacters.relationshipFields = Object.keys(extensionSettings.trackerConfig.presentCharacters.relationshipEmojis); renderPresentCharactersTab(); }); // Remove relationship $('.rpg-remove-relationship').off('click').on('click', function() { const relationship = $(this).data('relationship'); if (extensionSettings.trackerConfig.presentCharacters.relationshipEmojis) { delete extensionSettings.trackerConfig.presentCharacters.relationshipEmojis[relationship]; } // Sync relationshipFields extensionSettings.trackerConfig.presentCharacters.relationshipFields = Object.keys(extensionSettings.trackerConfig.presentCharacters.relationshipEmojis); renderPresentCharactersTab(); }); // Update relationship name $('.rpg-relationship-name').off('blur').on('blur', function() { const newName = $(this).val(); const $item = $(this).closest('.rpg-relationship-item'); const emoji = $item.find('.rpg-relationship-emoji').val(); // Find the old name by matching the emoji const oldName = Object.keys(extensionSettings.trackerConfig.presentCharacters.relationshipEmojis).find( key => extensionSettings.trackerConfig.presentCharacters.relationshipEmojis[key] === emoji && key !== newName ); if (oldName && oldName !== newName) { delete extensionSettings.trackerConfig.presentCharacters.relationshipEmojis[oldName]; extensionSettings.trackerConfig.presentCharacters.relationshipEmojis[newName] = emoji; // Sync relationshipFields extensionSettings.trackerConfig.presentCharacters.relationshipFields = Object.keys(extensionSettings.trackerConfig.presentCharacters.relationshipEmojis); } }); // Update relationship emoji $('.rpg-relationship-emoji').off('blur').on('blur', function() { const name = $(this).closest('.rpg-relationship-item').find('.rpg-relationship-name').val(); if (!extensionSettings.trackerConfig.presentCharacters.relationshipEmojis) { extensionSettings.trackerConfig.presentCharacters.relationshipEmojis = {}; } extensionSettings.trackerConfig.presentCharacters.relationshipEmojis[name] = $(this).val(); }); // Thoughts configuration $('#rpg-thoughts-enabled').off('change').on('change', function() { if (!extensionSettings.trackerConfig.presentCharacters.thoughts) { extensionSettings.trackerConfig.presentCharacters.thoughts = {}; } extensionSettings.trackerConfig.presentCharacters.thoughts.enabled = $(this).is(':checked'); }); $('#rpg-thoughts-name').off('blur').on('blur', function() { if (!extensionSettings.trackerConfig.presentCharacters.thoughts) { extensionSettings.trackerConfig.presentCharacters.thoughts = {}; } extensionSettings.trackerConfig.presentCharacters.thoughts.name = $(this).val(); }); $('#rpg-thoughts-description').off('blur').on('blur', function() { if (!extensionSettings.trackerConfig.presentCharacters.thoughts) { extensionSettings.trackerConfig.presentCharacters.thoughts = {}; } extensionSettings.trackerConfig.presentCharacters.thoughts.description = $(this).val(); }); // Add field $('#rpg-add-field').off('click').on('click', function() { extensionSettings.trackerConfig.presentCharacters.customFields.push({ id: 'custom_' + Date.now(), name: 'New Field', enabled: true, description: 'Description for AI' }); renderPresentCharactersTab(); }); // Remove field $('.rpg-field-remove').off('click').on('click', function() { const index = $(this).data('index'); extensionSettings.trackerConfig.presentCharacters.customFields.splice(index, 1); renderPresentCharactersTab(); }); // Move field up $('.rpg-field-move-up').off('click').on('click', function() { const index = $(this).data('index'); if (index > 0) { const fields = extensionSettings.trackerConfig.presentCharacters.customFields; [fields[index - 1], fields[index]] = [fields[index], fields[index - 1]]; renderPresentCharactersTab(); } }); // Move field down $('.rpg-field-move-down').off('click').on('click', function() { const index = $(this).data('index'); const fields = extensionSettings.trackerConfig.presentCharacters.customFields; if (index < fields.length - 1) { [fields[index], fields[index + 1]] = [fields[index + 1], fields[index]]; renderPresentCharactersTab(); } }); // Toggle field $('.rpg-field-toggle').off('change').on('change', function() { const index = $(this).data('index'); extensionSettings.trackerConfig.presentCharacters.customFields[index].enabled = $(this).is(':checked'); }); // Rename field $('.rpg-field-label').off('blur').on('blur', function() { const index = $(this).data('index'); extensionSettings.trackerConfig.presentCharacters.customFields[index].name = $(this).val(); }); // Update description $('.rpg-field-placeholder').off('blur').on('blur', function() { const index = $(this).data('index'); extensionSettings.trackerConfig.presentCharacters.customFields[index].description = $(this).val(); }); // Character stats toggle $('#rpg-char-stats-enabled').off('change').on('change', function() { if (!extensionSettings.trackerConfig.presentCharacters.characterStats) { extensionSettings.trackerConfig.presentCharacters.characterStats = { enabled: false, customStats: [] }; } extensionSettings.trackerConfig.presentCharacters.characterStats.enabled = $(this).is(':checked'); }); // Add character stat $('#rpg-add-char-stat').off('click').on('click', function() { if (!extensionSettings.trackerConfig.presentCharacters.characterStats) { extensionSettings.trackerConfig.presentCharacters.characterStats = { enabled: false, customStats: [] }; } if (!extensionSettings.trackerConfig.presentCharacters.characterStats.customStats) { extensionSettings.trackerConfig.presentCharacters.characterStats.customStats = []; } extensionSettings.trackerConfig.presentCharacters.characterStats.customStats.push({ id: `stat-${Date.now()}`, name: 'New Stat', enabled: true }); renderPresentCharactersTab(); }); // Remove character stat $('.rpg-char-stat-remove').off('click').on('click', function() { const index = $(this).data('index'); extensionSettings.trackerConfig.presentCharacters.characterStats.customStats.splice(index, 1); renderPresentCharactersTab(); }); // Toggle character stat $('.rpg-char-stat-toggle').off('change').on('change', function() { const index = $(this).data('index'); extensionSettings.trackerConfig.presentCharacters.characterStats.customStats[index].enabled = $(this).is(':checked'); }); // Rename character stat $('.rpg-char-stat-label').off('blur').on('blur', function() { const index = $(this).data('index'); extensionSettings.trackerConfig.presentCharacters.characterStats.customStats[index].name = $(this).val(); }); }