10cfe581ac
Add a new Equipment tab to manage player gear and stat bonuses. Features: - 19 equipment slots across 8 categories (helmet, necklace, body armor, gloves, pants, shoes, rings, accessories) - Type-to-slot validation: each type has max equipped limits (1 helmet, 10 rings, 3 accessories, etc.) - Auto-slot assignment: equipping a ring fills the first available ring slot - Stat bonuses from equipped items display on RPG attributes (e.g. STR 10 +2) - Create/edit modal with stat checkboxes per RPG attribute - Inventory list for unequipped items Architecture: - Shared constants in src/systems/equipment/constants.js - Category-based types (Ring, Accessory) with auto-slot assignment - v7 migration converts legacy slot-specific types to generic categories - Full i18n support for all UI strings Files: - New: src/systems/equipment/constants.js - New: src/systems/interaction/equipmentActions.js - New: src/systems/rendering/equipment.js - Modified: state.js, persistence.js, template.html, index.js - Modified: userStats.js, desktop.js, mobile.js, layout.js, modals.js - Modified: apiClient.js, sillytavern.js, style.css, en.json
731 lines
21 KiB
JavaScript
731 lines
21 KiB
JavaScript
/**
|
|
* Modal Management Module
|
|
* Handles DiceModal and SettingsModal ES6 classes with state management
|
|
*/
|
|
|
|
import { getContext } from '../../../../../../extensions.js';
|
|
import {
|
|
extensionSettings,
|
|
lastGeneratedData,
|
|
committedTrackerData,
|
|
$infoBoxContainer,
|
|
$thoughtsContainer,
|
|
$userStatsContainer,
|
|
clearThoughtBasedExpressionPortraits,
|
|
setPendingDiceRoll,
|
|
getPendingDiceRoll,
|
|
clearSessionAvatarPrompts
|
|
} from '../../core/state.js';
|
|
import { saveSettings, saveChatData } from '../../core/persistence.js';
|
|
import { renderUserStats } from '../rendering/userStats.js';
|
|
import { renderInfoBox } from '../rendering/infoBox.js';
|
|
import { renderThoughts, updateChatThoughts } from '../rendering/thoughts.js';
|
|
import { renderQuests } from '../rendering/quests.js';
|
|
import { renderInventory } from '../rendering/inventory.js';
|
|
import { renderEquipment } from '../rendering/equipment.js';
|
|
import {
|
|
rollDice as rollDiceCore,
|
|
clearDiceRoll as clearDiceRollCore,
|
|
updateDiceDisplay as updateDiceDisplayCore,
|
|
addDiceQuickReply as addDiceQuickReplyCore
|
|
} from '../features/dice.js';
|
|
import { i18n } from '../../core/i18n.js';
|
|
|
|
/**
|
|
* Modern DiceModal ES6 Class
|
|
* Manages dice roller modal with proper state management and CSS classes
|
|
*/
|
|
export class DiceModal {
|
|
constructor() {
|
|
this.modal = document.getElementById('rpg-dice-popup');
|
|
this.animation = document.getElementById('rpg-dice-animation');
|
|
this.result = document.getElementById('rpg-dice-result');
|
|
this.resultValue = document.getElementById('rpg-dice-result-value');
|
|
this.resultDetails = document.getElementById('rpg-dice-result-details');
|
|
this.rollBtn = document.getElementById('rpg-dice-roll-btn');
|
|
|
|
this.state = 'IDLE'; // IDLE, ROLLING, SHOWING_RESULT
|
|
this.isAnimating = false;
|
|
}
|
|
|
|
/**
|
|
* Opens the modal with proper animation
|
|
*/
|
|
open() {
|
|
if (this.isAnimating) return;
|
|
|
|
// Apply theme
|
|
const theme = extensionSettings.theme;
|
|
this.modal.setAttribute('data-theme', theme);
|
|
|
|
// Apply custom theme if needed
|
|
if (theme === 'custom') {
|
|
this._applyCustomTheme();
|
|
}
|
|
|
|
// Reset to initial state
|
|
this._setState('IDLE');
|
|
|
|
// Open modal with CSS class
|
|
this.modal.classList.add('is-open');
|
|
this.modal.classList.remove('is-closing');
|
|
|
|
// Focus management
|
|
this.modal.querySelector('#rpg-dice-popup-close')?.focus();
|
|
}
|
|
|
|
/**
|
|
* Closes the modal with animation
|
|
*/
|
|
close() {
|
|
if (this.isAnimating) return;
|
|
|
|
this.isAnimating = true;
|
|
this.modal.classList.add('is-closing');
|
|
this.modal.classList.remove('is-open');
|
|
|
|
// Wait for animation to complete
|
|
setTimeout(() => {
|
|
this.modal.classList.remove('is-closing');
|
|
this.isAnimating = false;
|
|
|
|
// Clear pending roll
|
|
setPendingDiceRoll(null);
|
|
}, 200);
|
|
}
|
|
|
|
/**
|
|
* Starts the rolling animation
|
|
*/
|
|
startRolling() {
|
|
this._setState('ROLLING');
|
|
}
|
|
|
|
/**
|
|
* Shows the result
|
|
* @param {number} total - The total roll value
|
|
* @param {Array<number>} rolls - Individual roll values
|
|
*/
|
|
showResult(total, rolls) {
|
|
this._setState('SHOWING_RESULT');
|
|
|
|
// Update result values
|
|
this.resultValue.textContent = total;
|
|
this.resultValue.classList.add('is-animating');
|
|
|
|
// Remove animation class after it completes
|
|
setTimeout(() => {
|
|
this.resultValue.classList.remove('is-animating');
|
|
}, 500);
|
|
|
|
// Show details if multiple rolls
|
|
if (rolls && rolls.length > 1) {
|
|
this.resultDetails.textContent = `Rolls: ${rolls.join(', ')}`;
|
|
} else {
|
|
this.resultDetails.textContent = '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Manages modal state changes
|
|
* @private
|
|
*/
|
|
_setState(newState) {
|
|
this.state = newState;
|
|
|
|
switch (newState) {
|
|
case 'IDLE':
|
|
this.rollBtn.hidden = false;
|
|
this.animation.hidden = true;
|
|
this.result.hidden = true;
|
|
break;
|
|
|
|
case 'ROLLING':
|
|
this.rollBtn.hidden = true;
|
|
this.animation.hidden = false;
|
|
this.result.hidden = true;
|
|
this.animation.setAttribute('aria-busy', 'true');
|
|
break;
|
|
|
|
case 'SHOWING_RESULT':
|
|
this.rollBtn.hidden = true;
|
|
this.animation.hidden = true;
|
|
this.result.hidden = false;
|
|
this.animation.setAttribute('aria-busy', 'false');
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Applies custom theme colors
|
|
* @private
|
|
*/
|
|
_applyCustomTheme() {
|
|
const content = this.modal.querySelector('.rpg-dice-popup-content');
|
|
if (content && extensionSettings.customColors) {
|
|
content.style.setProperty('--rpg-bg', extensionSettings.customColors.bg);
|
|
content.style.setProperty('--rpg-accent', extensionSettings.customColors.accent);
|
|
content.style.setProperty('--rpg-text', extensionSettings.customColors.text);
|
|
content.style.setProperty('--rpg-highlight', extensionSettings.customColors.highlight);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SettingsModal - Manages the settings popup modal
|
|
* Handles opening, closing, theming, and animations
|
|
*/
|
|
export class SettingsModal {
|
|
constructor() {
|
|
this.modal = document.getElementById('rpg-settings-popup');
|
|
this.content = this.modal?.querySelector('.rpg-settings-popup-content');
|
|
this.isAnimating = false;
|
|
}
|
|
|
|
/**
|
|
* Opens the modal with proper animation
|
|
*/
|
|
open() {
|
|
if (this.isAnimating || !this.modal) return;
|
|
|
|
// Apply theme
|
|
const theme = extensionSettings.theme || 'default';
|
|
this.modal.setAttribute('data-theme', theme);
|
|
|
|
// Apply custom theme if needed
|
|
if (theme === 'custom') {
|
|
this._applyCustomTheme();
|
|
}
|
|
|
|
// Open modal with CSS class
|
|
this.modal.classList.add('is-open');
|
|
this.modal.classList.remove('is-closing');
|
|
|
|
// Focus management
|
|
this.modal.querySelector('#rpg-close-settings')?.focus();
|
|
}
|
|
|
|
/**
|
|
* Closes the modal with animation
|
|
*/
|
|
close() {
|
|
if (this.isAnimating || !this.modal) return;
|
|
|
|
this.isAnimating = true;
|
|
this.modal.classList.add('is-closing');
|
|
this.modal.classList.remove('is-open');
|
|
|
|
// Wait for animation to complete
|
|
setTimeout(() => {
|
|
this.modal.classList.remove('is-closing');
|
|
this.isAnimating = false;
|
|
}, 200);
|
|
}
|
|
|
|
/**
|
|
* Updates the theme in real-time (used when theme selector changes)
|
|
*/
|
|
updateTheme() {
|
|
if (!this.modal) return;
|
|
|
|
const theme = extensionSettings.theme || 'default';
|
|
this.modal.setAttribute('data-theme', theme);
|
|
|
|
if (theme === 'custom') {
|
|
this._applyCustomTheme();
|
|
} else {
|
|
// Clear custom CSS variables to let theme CSS take over
|
|
this._clearCustomTheme();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Applies custom theme colors
|
|
* @private
|
|
*/
|
|
_applyCustomTheme() {
|
|
if (!this.content || !extensionSettings.customColors) return;
|
|
|
|
this.content.style.setProperty('--rpg-bg', extensionSettings.customColors.bg);
|
|
this.content.style.setProperty('--rpg-accent', extensionSettings.customColors.accent);
|
|
this.content.style.setProperty('--rpg-text', extensionSettings.customColors.text);
|
|
this.content.style.setProperty('--rpg-highlight', extensionSettings.customColors.highlight);
|
|
}
|
|
|
|
/**
|
|
* Clears custom theme colors
|
|
* @private
|
|
*/
|
|
_clearCustomTheme() {
|
|
if (!this.content) return;
|
|
|
|
this.content.style.setProperty('--rpg-bg', '');
|
|
this.content.style.setProperty('--rpg-accent', '');
|
|
this.content.style.setProperty('--rpg-text', '');
|
|
this.content.style.setProperty('--rpg-highlight', '');
|
|
}
|
|
}
|
|
|
|
// Global instances
|
|
let diceModal = null;
|
|
let settingsModal = null;
|
|
|
|
/**
|
|
* Sets up the dice roller functionality.
|
|
* @returns {DiceModal} The initialized DiceModal instance
|
|
*/
|
|
export function setupDiceRoller() {
|
|
// Initialize DiceModal instance
|
|
diceModal = new DiceModal();
|
|
|
|
// Click dice display to open popup
|
|
$('#rpg-dice-display').on('click', function() {
|
|
openDicePopup();
|
|
});
|
|
|
|
// Close popup - handle both close button and backdrop clicks
|
|
$('#rpg-dice-popup-close').on('click', function() {
|
|
closeDicePopup();
|
|
});
|
|
|
|
// Close on backdrop click (clicking outside content)
|
|
$('#rpg-dice-popup').on('click', function(e) {
|
|
if (e.target === this) {
|
|
closeDicePopup();
|
|
}
|
|
});
|
|
|
|
// Roll dice button
|
|
$('#rpg-dice-roll-btn').on('click', async function() {
|
|
await rollDiceCore(diceModal);
|
|
});
|
|
|
|
// Save roll button (closes popup and saves the roll)
|
|
$('#rpg-dice-save-btn').on('click', function() {
|
|
// Save the pending roll
|
|
const roll = getPendingDiceRoll();
|
|
if (roll) {
|
|
extensionSettings.lastDiceRoll = roll;
|
|
saveSettings();
|
|
updateDiceDisplayCore();
|
|
setPendingDiceRoll(null);
|
|
}
|
|
closeDicePopup();
|
|
});
|
|
|
|
// Reset on Enter key
|
|
$('#rpg-dice-count, #rpg-dice-sides').on('keypress', function(e) {
|
|
if (e.which === 13) {
|
|
rollDiceCore(diceModal);
|
|
}
|
|
});
|
|
|
|
// Clear dice roll button
|
|
$('#rpg-clear-dice').on('click', function(e) {
|
|
e.stopPropagation(); // Prevent opening the dice popup
|
|
clearDiceRollCore();
|
|
});
|
|
$('#rpg-clear-dice').attr('title', i18n.getTranslation('template.mainPanel.clearLastRoll') || 'Clear last roll');
|
|
|
|
return diceModal;
|
|
}
|
|
|
|
/**
|
|
* Sets up the settings popup functionality.
|
|
* @returns {SettingsModal} The initialized SettingsModal instance
|
|
*/
|
|
export function setupSettingsPopup() {
|
|
// Initialize SettingsModal instance
|
|
settingsModal = new SettingsModal();
|
|
|
|
// Open settings popup
|
|
$('#rpg-open-settings').on('click', function() {
|
|
openSettingsPopup();
|
|
});
|
|
|
|
// Close settings popup - close button
|
|
$('#rpg-close-settings').on('click', function() {
|
|
closeSettingsPopup();
|
|
});
|
|
|
|
// Close on backdrop click (clicking outside content)
|
|
$('#rpg-settings-popup').on('click', function(e) {
|
|
if (e.target === this) {
|
|
closeSettingsPopup();
|
|
}
|
|
});
|
|
|
|
// Clear cache button
|
|
$('#rpg-clear-cache').on('click', function() {
|
|
// console.log('[RPG Companion] Clear Cache button clicked');
|
|
|
|
// Clear the data (set to null so panels show "not generated yet")
|
|
lastGeneratedData.userStats = null;
|
|
lastGeneratedData.infoBox = null;
|
|
lastGeneratedData.characterThoughts = null;
|
|
lastGeneratedData.html = null;
|
|
|
|
// Clear committed tracker data (used for generation context)
|
|
committedTrackerData.userStats = null;
|
|
committedTrackerData.infoBox = null;
|
|
committedTrackerData.characterThoughts = null;
|
|
|
|
// Clear session avatar prompts
|
|
clearSessionAvatarPrompts();
|
|
clearThoughtBasedExpressionPortraits();
|
|
|
|
// Clear chat metadata immediately (don't wait for debounced save)
|
|
const context = getContext();
|
|
if (context.chat_metadata && context.chat_metadata.rpg_companion) {
|
|
delete context.chat_metadata.rpg_companion;
|
|
// console.log('[RPG Companion] Cleared chat_metadata.rpg_companion for current chat');
|
|
}
|
|
|
|
// Clear all message swipe data
|
|
const chat = context.chat;
|
|
if (chat && chat.length > 0) {
|
|
for (let i = 0; i < chat.length; i++) {
|
|
const message = chat[i];
|
|
if (message.extra && message.extra.rpg_companion_swipes) {
|
|
delete message.extra.rpg_companion_swipes;
|
|
// console.log('[RPG Companion] Cleared swipe data from message at index', i);
|
|
}
|
|
|
|
if (Array.isArray(message.swipe_info)) {
|
|
for (const swipeInfo of message.swipe_info) {
|
|
if (swipeInfo?.extra?.rpg_companion_swipes) {
|
|
delete swipeInfo.extra.rpg_companion_swipes;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clear the UI
|
|
if ($infoBoxContainer) {
|
|
$infoBoxContainer.empty();
|
|
}
|
|
if ($thoughtsContainer) {
|
|
$thoughtsContainer.empty();
|
|
}
|
|
if ($userStatsContainer) {
|
|
$userStatsContainer.empty();
|
|
}
|
|
|
|
// Reset user stats to default object structure (extensionSettings stores as object, not JSON string)
|
|
extensionSettings.userStats = {
|
|
health: 100,
|
|
satiety: 100,
|
|
energy: 100,
|
|
hygiene: 100,
|
|
arousal: 0,
|
|
mood: '😐',
|
|
conditions: 'None',
|
|
skills: [],
|
|
inventory: {
|
|
version: 2,
|
|
onPerson: "None",
|
|
clothing: "None",
|
|
stored: {},
|
|
assets: "None"
|
|
}
|
|
};
|
|
|
|
// Reset info box to defaults (as object)
|
|
extensionSettings.infoBox = {
|
|
date: new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }),
|
|
weather: '☀️ Clear skies',
|
|
temperature: '20°C',
|
|
time: '00:00 - 00:00',
|
|
location: 'Unknown Location',
|
|
recentEvents: []
|
|
};
|
|
|
|
// Reset character thoughts to empty (as object)
|
|
extensionSettings.characterThoughts = {
|
|
characters: []
|
|
};
|
|
|
|
// Reset classic stats (attributes) to defaults
|
|
extensionSettings.classicStats = {
|
|
str: 10,
|
|
dex: 10,
|
|
con: 10,
|
|
int: 10,
|
|
wis: 10,
|
|
cha: 10
|
|
};
|
|
|
|
// Clear dice roll
|
|
extensionSettings.lastDiceRoll = null;
|
|
|
|
// Reset level to 1
|
|
extensionSettings.level = 1;
|
|
|
|
// Clear quests
|
|
extensionSettings.quests = {
|
|
main: "None",
|
|
optional: []
|
|
};
|
|
|
|
// Clear all locked items
|
|
extensionSettings.lockedItems = {
|
|
stats: [],
|
|
skills: [],
|
|
inventory: {
|
|
onPerson: [],
|
|
clothing: [],
|
|
stored: {},
|
|
assets: []
|
|
},
|
|
quests: {
|
|
main: false,
|
|
optional: []
|
|
},
|
|
infoBox: {
|
|
date: false,
|
|
weather: false,
|
|
temperature: false,
|
|
time: false,
|
|
location: false,
|
|
recentEvents: false
|
|
},
|
|
characters: {}
|
|
};
|
|
|
|
// Save everything
|
|
saveChatData();
|
|
saveSettings();
|
|
|
|
// Re-render all panels - they will show "not generated yet" messages since data is null
|
|
renderUserStats();
|
|
renderInfoBox();
|
|
renderThoughts();
|
|
updateDiceDisplayCore();
|
|
updateChatThoughts();
|
|
renderInventory();
|
|
renderEquipment();
|
|
renderQuests();
|
|
|
|
// console.log('[RPG Companion] Cache cleared successfully');
|
|
});
|
|
|
|
return settingsModal;
|
|
}
|
|
|
|
/**
|
|
* Opens the dice rolling popup.
|
|
* Backwards compatible wrapper for DiceModal class.
|
|
*/
|
|
export function openDicePopup() {
|
|
if (diceModal) {
|
|
diceModal.open();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Closes the dice rolling popup.
|
|
* Backwards compatible wrapper for DiceModal class.
|
|
*/
|
|
export function closeDicePopup() {
|
|
if (diceModal) {
|
|
diceModal.close();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Opens the settings popup.
|
|
* Backwards compatible wrapper for SettingsModal class.
|
|
*/
|
|
export function openSettingsPopup() {
|
|
if (settingsModal) {
|
|
settingsModal.open();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Closes the settings popup.
|
|
* Backwards compatible wrapper for SettingsModal class.
|
|
*/
|
|
export function closeSettingsPopup() {
|
|
if (settingsModal) {
|
|
settingsModal.close();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @deprecated Legacy function - use diceModal._applyCustomTheme() instead
|
|
*/
|
|
export function applyCustomThemeToPopup() {
|
|
if (diceModal) {
|
|
diceModal._applyCustomTheme();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clears the last dice roll.
|
|
* Backwards compatible wrapper for dice module.
|
|
*/
|
|
export function clearDiceRoll() {
|
|
clearDiceRollCore();
|
|
}
|
|
|
|
/**
|
|
* Updates the dice display in the sidebar.
|
|
* Backwards compatible wrapper for dice module.
|
|
*/
|
|
export function updateDiceDisplay() {
|
|
updateDiceDisplayCore();
|
|
}
|
|
|
|
/**
|
|
* Adds the Roll Dice quick reply button.
|
|
* Backwards compatible wrapper for dice module.
|
|
*/
|
|
export function addDiceQuickReply() {
|
|
addDiceQuickReplyCore();
|
|
}
|
|
|
|
/**
|
|
* Returns the SettingsModal instance for external use
|
|
* @returns {SettingsModal} The global SettingsModal instance
|
|
*/
|
|
export function getSettingsModal() {
|
|
return settingsModal;
|
|
}
|
|
|
|
/**
|
|
* Shows the welcome modal for v3.0.0 on first launch
|
|
* Checks if user has already seen this version's welcome screen
|
|
*/
|
|
export function showWelcomeModalIfNeeded() {
|
|
const WELCOME_VERSION = '3.0.1';
|
|
const STORAGE_KEY = 'rpg_companion_welcome_seen';
|
|
|
|
try {
|
|
const seenVersion = localStorage.getItem(STORAGE_KEY);
|
|
|
|
// If user hasn't seen v3.0.0 welcome yet, show it
|
|
if (seenVersion !== WELCOME_VERSION) {
|
|
showWelcomeModal(WELCOME_VERSION, STORAGE_KEY);
|
|
}
|
|
} catch (error) {
|
|
console.error('[RPG Companion] Failed to check welcome modal status:', error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Shows the deprecation notice once for users updating to the deprecation release.
|
|
* @returns {boolean} True when the modal was displayed.
|
|
*/
|
|
export function showDeprecationModalIfNeeded() {
|
|
const DEPRECATION_NOTICE_VERSION = '3.7.4';
|
|
const STORAGE_KEY = 'rpg_companion_deprecation_notice_seen';
|
|
|
|
try {
|
|
const seenVersion = localStorage.getItem(STORAGE_KEY);
|
|
|
|
if (seenVersion !== DEPRECATION_NOTICE_VERSION) {
|
|
showDeprecationModal(DEPRECATION_NOTICE_VERSION, STORAGE_KEY);
|
|
return true;
|
|
}
|
|
} catch (error) {
|
|
console.error('[RPG Companion] Failed to check deprecation modal status:', error);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Shows the welcome modal
|
|
* @param {string} version - The version to mark as seen
|
|
* @param {string} storageKey - The localStorage key to use
|
|
*/
|
|
function showWelcomeModal(version, storageKey) {
|
|
const modal = document.getElementById('rpg-welcome-modal');
|
|
if (!modal) {
|
|
console.error('[RPG Companion] Welcome modal element not found');
|
|
return;
|
|
}
|
|
|
|
// Apply current theme to modal
|
|
const theme = extensionSettings.theme || 'default';
|
|
modal.setAttribute('data-theme', theme);
|
|
|
|
// Show modal
|
|
modal.style.display = 'flex';
|
|
modal.classList.add('is-open');
|
|
|
|
// Close button handler
|
|
const closeBtn = document.getElementById('rpg-welcome-close');
|
|
const gotItBtn = document.getElementById('rpg-welcome-got-it');
|
|
|
|
const closeModal = () => {
|
|
modal.classList.add('is-closing');
|
|
|
|
setTimeout(() => {
|
|
modal.style.display = 'none';
|
|
modal.classList.remove('is-open', 'is-closing');
|
|
}, 200);
|
|
|
|
// Mark this version as seen
|
|
try {
|
|
localStorage.setItem(storageKey, version);
|
|
} catch (error) {
|
|
console.error('[RPG Companion] Failed to save welcome modal status:', error);
|
|
}
|
|
};
|
|
|
|
// Attach event listeners
|
|
closeBtn?.addEventListener('click', closeModal, { once: true });
|
|
gotItBtn?.addEventListener('click', closeModal, { once: true });
|
|
|
|
// Close on background click
|
|
modal.addEventListener('click', (e) => {
|
|
if (e.target === modal) {
|
|
closeModal();
|
|
}
|
|
}, { once: true });
|
|
}
|
|
|
|
function showDeprecationModal(version, storageKey) {
|
|
const modal = document.getElementById('rpg-deprecation-modal');
|
|
if (!modal) {
|
|
console.error('[RPG Companion] Deprecation modal element not found');
|
|
return;
|
|
}
|
|
|
|
const theme = extensionSettings.theme || 'default';
|
|
modal.setAttribute('data-theme', theme);
|
|
|
|
modal.style.display = 'flex';
|
|
modal.classList.add('is-open');
|
|
|
|
const closeBtn = document.getElementById('rpg-deprecation-close');
|
|
const gotItBtn = document.getElementById('rpg-deprecation-got-it');
|
|
|
|
const closeModal = () => {
|
|
modal.classList.add('is-closing');
|
|
|
|
setTimeout(() => {
|
|
modal.style.display = 'none';
|
|
modal.classList.remove('is-open', 'is-closing');
|
|
}, 200);
|
|
|
|
try {
|
|
localStorage.setItem(storageKey, version);
|
|
} catch (error) {
|
|
console.error('[RPG Companion] Failed to save deprecation modal status:', error);
|
|
}
|
|
};
|
|
|
|
closeBtn?.addEventListener('click', closeModal, { once: true });
|
|
gotItBtn?.addEventListener('click', closeModal, { once: true });
|
|
|
|
modal.addEventListener('click', (e) => {
|
|
if (e.target === modal) {
|
|
closeModal();
|
|
}
|
|
}, { once: true });
|
|
}
|