Files
ai.interactive.fiction/public/js/options-ui-module.js
T

964 lines
34 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Options UI Module
* Provides the options UI for the game
*/
import { BaseModule } from './base-module.js';
import { createUIElement, populateDropdown, registerHandler, createPreferenceBinding } from './ui-helper.js';
class OptionsUIModule extends BaseModule {
/**
* Create a new options UI module
*/
constructor() {
super('options-ui', 'Options UI');
// Set up dependencies
this.dependencies = [
'persistence-manager',
'localization',
'tts-factory',
'audio-manager'
];
// Modal element
this.modal = null;
// UI elements
this.elements = {};
// Settings that require reload
this.reloadRequired = false;
// Bind methods
this.bindMethods([
'show',
'hide',
'createModal',
'populateTtsSystems',
'populateVoices',
'populateLanguages',
'loadPreferences',
'applySettings',
'handleTtsSystemChanged',
'showReloadNotice',
'toggle',
'setupEventListeners',
'saveCurrentSettings',
'setupApiUrlFields',
'setupInitialState',
'dispatchApiChangeEvent',
'getPreference',
'updatePreference'
]);
}
/**
* Dispatches an API change event
* @param {string} eventType - Event type (e.g. 'api:key:change')
* @param {string} provider - Provider name (e.g. 'elevenlabs')
* @param {string} valueType - Value type (e.g. 'key', 'url')
* @param {string} value - Value to dispatch
*/
dispatchApiChangeEvent(eventType, provider, valueType, value) {
const eventName = `tts:${eventType}`;
console.log(`Options UI: Dispatching event ${eventName} for provider ${provider}`);
document.dispatchEvent(new CustomEvent(eventName, {
detail: { provider, [valueType]: value }
}));
}
/**
* Gets a preference from the persistence manager
* @param {string} category - Preference category
* @param {string} key - Preference key
* @param {*} defaultValue - Default value if preference doesn't exist
* @returns {*} - Preference value
*/
getPreference(category, key, defaultValue) {
const persistenceManager = this.getModule('persistence-manager');
return persistenceManager.getPreference(category, key, defaultValue);
}
/**
* Updates a preference in the persistence manager
* @param {string} category - Preference category
* @param {string} key - Preference key
* @param {*} value - Value to set
*/
updatePreference(category, key, value) {
const persistenceManager = this.getModule('persistence-manager');
persistenceManager.updatePreference(category, key, value);
}
/**
* Initialize the Options UI module
* @returns {Promise<boolean>} - Promise resolves with initialization success
*/
async initialize() {
console.log('Options UI: Initializing');
// Create DOM elements
this.createModal();
// Set up event listeners
this.setupEventListeners();
// Set up API URL fields
this.setupApiUrlFields();
// Set up initial state
await this.setupInitialState();
// Set up immediate save listeners
this.setupImmediateSaveListeners();
this.reportProgress(100, 'Options UI initialized');
return true;
}
/**
* Create the options modal
*/
createModal() {
if (this.modal) return;
const body = document.body;
// Create modal container
this.modal = createUIElement('div', { className: 'options-modal', id: 'options-modal' }, null, body);
// Create modal content
const modalContent = createUIElement('div', { className: 'options-content' }, null, this.modal);
// Create header
const header = createUIElement('div', { className: 'options-header' }, null, modalContent);
createUIElement('h2', {}, 'Options', header);
this.elements.closeButton = createUIElement('button', { className: 'options-close', 'aria-label': 'Close' }, '×', header);
// Create settings container
const settings = createUIElement('div', { className: 'options-settings' }, null, modalContent);
// Language Section
const languageSection = createUIElement('div', { className: 'options-section' }, null, settings);
createUIElement('h3', {}, 'Language Settings', languageSection);
// Language selection
const languageContainer = createUIElement('div', { className: 'options-row' }, null, languageSection);
createUIElement('label', {}, 'Language:', languageContainer);
this.elements.language = createUIElement('select', { id: 'app-language' }, null, languageContainer);
// TTS Settings
const ttsSection = createUIElement('div', { className: 'options-section' }, null, settings);
createUIElement('h3', {}, 'Text-to-Speech', ttsSection);
// TTS Toggle
const ttsSpeechToggleContainer = createUIElement('div', { className: 'options-row' }, null, ttsSection);
createUIElement('label', {}, 'Enable Text-to-Speech:', ttsSpeechToggleContainer);
this.elements.ttsEnabled = createUIElement('input', { type: 'checkbox', id: 'tts-enabled' }, null, ttsSpeechToggleContainer);
// TTS System
const ttsSystemContainer = createUIElement('div', { className: 'options-row' }, null, ttsSection);
createUIElement('label', {}, 'TTS System:', ttsSystemContainer);
this.elements.ttsSystem = createUIElement('select', { id: 'tts-system' }, null, ttsSystemContainer);
// TTS Voice
const ttsVoiceContainer = createUIElement('div', { className: 'options-row' }, null, ttsSection);
createUIElement('label', {}, 'Voice:', ttsVoiceContainer);
this.elements.ttsVoice = createUIElement('select', { id: 'tts-voice' }, null, ttsVoiceContainer);
// TTS Speed
const speedContainer = createUIElement('div', { className: 'options-row' }, null, ttsSection);
createUIElement('label', {}, 'TTS Speed:', speedContainer);
this.elements.ttsSpeed = createUIElement('input', {
type: 'range',
id: 'tts-speed',
min: '0',
max: '100'
}, null, speedContainer);
// Create API settings for each provider
const apiSettings = this.createApiSettings(ttsSection);
// Audio Settings Section
const audioSection = createUIElement('div', { className: 'options-section' }, null, settings);
createUIElement('h3', {}, 'Audio', audioSection);
// Master Volume
const masterVolumeContainer = createUIElement('div', { className: 'options-row' }, null, audioSection);
createUIElement('label', {}, 'Master Volume:', masterVolumeContainer);
this.elements.masterVolume = createUIElement('input', {
type: 'range',
id: 'master-volume',
min: '0',
max: '100'
}, null, masterVolumeContainer);
// Music Volume
const musicVolumeContainer = createUIElement('div', { className: 'options-row' }, null, audioSection);
createUIElement('label', {}, 'Music Volume:', musicVolumeContainer);
this.elements.musicVolume = createUIElement('input', {
type: 'range',
id: 'music-volume',
min: '0',
max: '100'
}, null, musicVolumeContainer);
// SFX Volume
const sfxVolumeContainer = createUIElement('div', { className: 'options-row' }, null, audioSection);
createUIElement('label', {}, 'Sound Effects Volume:', sfxVolumeContainer);
this.elements.sfxVolume = createUIElement('input', {
type: 'range',
id: 'sfx-volume',
min: '0',
max: '100'
}, null, sfxVolumeContainer);
// Ambience Volume
const ambienceVolumeContainer = createUIElement('div', { className: 'options-row' }, null, audioSection);
createUIElement('label', {}, 'Ambience Volume:', ambienceVolumeContainer);
this.elements.ambienceVolume = createUIElement('input', {
type: 'range',
id: 'ambience-volume',
min: '0',
max: '100'
}, null, ambienceVolumeContainer);
// Initialize with display: none
this.modal.style.display = 'none';
// Add event handlers
this.elements.closeButton.addEventListener('click', () => {
this.saveCurrentSettings();
this.hide();
});
}
/**
* Create API settings for TTS providers
* @param {HTMLElement} parentSection - Parent section for API settings
* @returns {Object} - Object with API settings elements
*/
createApiSettings(parentSection) {
// ElevenLabs settings
// API Key
const elevenLabsApiKeyContainer = createUIElement('div', {
className: 'options-row elevenlabs-tts-setting',
'data-provider': 'elevenlabs-tts'
}, null, parentSection);
createUIElement('label', {}, 'ElevenLabs API Key:', elevenLabsApiKeyContainer);
this.elements.elevenLabsApiKey = createUIElement('input', {
type: 'password',
placeholder: 'Enter your ElevenLabs API key'
}, null, elevenLabsApiKeyContainer);
// API URL
const elevenLabsApiUrlContainer = createUIElement('div', {
className: 'options-row elevenlabs-tts-setting',
'data-provider': 'elevenlabs-tts'
}, null, parentSection);
createUIElement('label', {}, 'ElevenLabs API URL:', elevenLabsApiUrlContainer);
this.elements.elevenLabsApiUrl = createUIElement('input', {
type: 'text',
placeholder: 'https://api.elevenlabs.io/v1'
}, null, elevenLabsApiUrlContainer);
// OpenAI settings
// API Key
const openaiApiKeyContainer = createUIElement('div', {
className: 'options-row openai-tts-setting',
'data-provider': 'openai-tts'
}, null, parentSection);
createUIElement('label', {}, 'OpenAI API Key:', openaiApiKeyContainer);
this.elements.openaiApiKey = createUIElement('input', {
type: 'password',
placeholder: 'Enter your OpenAI API key'
}, null, openaiApiKeyContainer);
// API URL
const openaiApiUrlContainer = createUIElement('div', {
className: 'options-row openai-tts-setting',
'data-provider': 'openai-tts'
}, null, parentSection);
createUIElement('label', {}, 'OpenAI API URL:', openaiApiUrlContainer);
this.elements.openaiApiUrl = createUIElement('input', {
type: 'text',
placeholder: 'https://api.openai.com/v1'
}, null, openaiApiUrlContainer);
// Initially hide API settings
const apiSettings = document.querySelectorAll('.elevenlabs-tts-setting, .openai-tts-setting');
apiSettings.forEach(setting => {
setting.style.display = 'none';
});
return { elevenLabsApiKeyContainer, elevenLabsApiUrlContainer, openaiApiKeyContainer, openaiApiUrlContainer };
}
/**
* Set up event listeners for UI elements
*/
setupEventListeners() {
// TTS System change event
if (this.elements.ttsSystem) {
this.elements.ttsSystem.addEventListener('change', this.handleTtsSystemChanged);
}
// TTS Enable toggle event
if (this.elements.ttsEnabled) {
this.elements.ttsEnabled.addEventListener('change', (event) => {
const enabled = event.target.checked;
console.log('Options UI: TTS enabled changed to', enabled);
// Save setting
this.updatePreference('tts', 'enabled', enabled);
// Update TTS Factory
const ttsFactory = this.getModule('tts-factory');
if (ttsFactory) {
ttsFactory.configure({ enabled });
}
});
}
// Voice change event
if (this.elements.ttsVoice) {
this.elements.ttsVoice.addEventListener('change', (event) => {
const voice = event.target.value;
console.log('Options UI: TTS voice changed to', voice);
// Save setting
this.updatePreference('tts', 'voice', voice);
// Update TTS Factory
const ttsFactory = this.getModule('tts-factory');
if (ttsFactory) {
ttsFactory.configure({ voice });
}
});
}
// TTS Speed change event
if (this.elements.ttsSpeed) {
this.elements.ttsSpeed.addEventListener('input', (event) => {
const speed = parseInt(event.target.value) / 100;
console.log('Options UI: TTS speed changed to', speed);
// Save setting
this.updatePreference('tts', 'speed', speed);
// Update TTS Factory
const ttsFactory = this.getModule('tts-factory');
if (ttsFactory) {
ttsFactory.configure({ speed });
}
});
}
// Language change event
if (this.elements.language) {
this.elements.language.addEventListener('change', (event) => {
const locale = event.target.value;
console.log('Options UI: Language changed to', locale);
// Save settings
this.updatePreference('app', 'locale', locale);
// Update Localization module
const localization = this.getModule('localization');
if (localization) {
localization.setLocale(locale);
}
// Show reload notice
this.showReloadNotice();
});
}
// Audio Settings
// Master Volume
if (this.elements.masterVolume) {
this.elements.masterVolume.addEventListener('input', (event) => {
const volume = parseInt(event.target.value) / 100;
console.log('Options UI: Master volume changed to', volume);
// Save setting
this.updatePreference('audio', 'masterVolume', volume);
// Update Audio Manager
const audioManager = this.getModule('audio-manager');
if (audioManager) {
audioManager.setMasterVolume(volume);
}
});
}
// Music Volume
if (this.elements.musicVolume) {
this.elements.musicVolume.addEventListener('input', (event) => {
const volume = parseInt(event.target.value) / 100;
console.log('Options UI: Music volume changed to', volume);
// Save setting
this.updatePreference('audio', 'musicVolume', volume);
// Update Audio Manager
const audioManager = this.getModule('audio-manager');
if (audioManager) {
audioManager.setMusicVolume(volume);
}
});
}
// SFX Volume
if (this.elements.sfxVolume) {
this.elements.sfxVolume.addEventListener('input', (event) => {
const volume = parseInt(event.target.value) / 100;
console.log('Options UI: SFX volume changed to', volume);
// Save setting
this.updatePreference('audio', 'sfxVolume', volume);
// Update Audio Manager
const audioManager = this.getModule('audio-manager');
if (audioManager) {
audioManager.setSfxVolume(volume);
}
});
}
}
/**
* Handle TTS system change
* @param {Event} event - Change event
*/
async handleTtsSystemChanged(event) {
const selectedSystem = event.target.value;
console.log('Options UI: TTS system changed to', selectedSystem);
// Update API settings visibility
this.updateApiSettingsVisibility(selectedSystem);
// Save setting
this.updatePreference('tts', 'preferred_handler', selectedSystem);
// Notify TTSFactory of handler change
const ttsFactory = this.getModule('tts-factory');
if (ttsFactory) {
await ttsFactory.setActiveHandler(selectedSystem);
// Now that the handler has changed, update voices for the selected system
await this.populateVoices();
}
}
/**
* Update API settings visibility based on selected TTS system
* @param {string} selectedSystem - Selected TTS system
*/
updateApiSettingsVisibility(selectedSystem) {
const elevenLabsSettings = document.querySelectorAll('.elevenlabs-tts-setting');
const openaiSettings = document.querySelectorAll('.openai-tts-setting');
elevenLabsSettings.forEach(setting => {
setting.style.display = selectedSystem === 'elevenlabs-tts' ? 'flex' : 'none';
});
openaiSettings.forEach(setting => {
setting.style.display = selectedSystem === 'openai-tts' ? 'flex' : 'none';
});
}
/**
* Show the options UI
*/
show() {
if (this.modal) {
this.modal.style.display = 'flex';
document.body.classList.add('modal-open');
}
}
/**
* Hide the options UI
*/
hide() {
if (this.modal) {
this.modal.style.display = 'none';
document.body.classList.remove('modal-open');
}
}
/**
* Toggle the options UI visibility
*/
toggle() {
if (this.modal) {
if (this.modal.style.display === 'flex') {
this.hide();
} else {
this.show();
}
}
}
/**
* Populate the TTS systems dropdown
*/
async populateTtsSystems() {
const ttsFactory = this.getModule('tts-factory');
if (!ttsFactory || !this.elements.ttsSystem) return;
// Get available TTS systems
const handlers = ttsFactory.getAvailableHandlers();
console.log('Options UI: Available TTS handlers:', handlers);
// Format for display
const systems = handlers.map(handler => ({
id: handler.id,
name: handler.displayName || handler.id
}));
// Populate dropdown
populateDropdown(
this.elements.ttsSystem,
systems,
'id',
'name',
this.getPreference('tts', 'preferred_handler', 'none')
);
// Update API settings visibility
this.updateApiSettingsVisibility(this.elements.ttsSystem.value);
}
/**
* Populate the voices dropdown
*/
async populateVoices() {
const ttsFactory = this.getModule('tts-factory');
if (!ttsFactory || !this.elements.ttsVoice) return;
// Get voices for current TTS system
const voices = await ttsFactory.getVoices() || [];
console.log('Options UI: TTS voices:', voices);
// Populate dropdown
populateDropdown(
this.elements.ttsVoice,
voices,
'id',
'name',
this.getPreference('tts', 'voice', '')
);
}
/**
* Populate the languages dropdown
*/
async populateLanguages() {
const localization = this.getModule('localization');
if (!localization || !this.elements.language) return;
// Get available languages
const languages = localization.getAvailableLocales() || [];
console.log('Options UI: Available languages:', languages);
// Format languages with their names
const languageOptions = languages.map(code => ({
code,
name: localization.getLanguageName(code)
}));
// Populate dropdown
populateDropdown(
this.elements.language,
languageOptions,
'code',
'name',
this.getPreference('app', 'locale', 'en-us')
);
}
/**
* Load user preferences from the persistence manager
*/
loadPreferences() {
const persistenceManager = this.getModule('persistence-manager');
if (!persistenceManager) return;
console.log('Options UI: Loading preferences');
// TTS Settings
// TTS Enable
if (this.elements.ttsEnabled) {
this.elements.ttsEnabled.checked = this.getPreference('tts', 'enabled', true);
}
// TTS System
if (this.elements.ttsSystem) {
const preferredHandler = this.getPreference('tts', 'preferred_handler', 'none');
if (this.elements.ttsSystem.querySelector(`option[value="${preferredHandler}"]`)) {
this.elements.ttsSystem.value = preferredHandler;
}
}
// TTS Speed
if (this.elements.ttsSpeed) {
const speed = this.getPreference('tts', 'speed', 1);
this.elements.ttsSpeed.value = Math.round(speed * 100);
}
// API Keys and URLs
// ElevenLabs API Key
if (this.elements.elevenLabsApiKey) {
this.elements.elevenLabsApiKey.value = this.getPreference('tts', 'elevenlabs-tts_api_key', '');
}
// ElevenLabs API URL
if (this.elements.elevenLabsApiUrl) {
this.elements.elevenLabsApiUrl.value = this.getPreference('tts', 'elevenlabs-tts_api_url', 'https://api.elevenlabs.io/v1');
}
// OpenAI API Key
if (this.elements.openaiApiKey) {
this.elements.openaiApiKey.value = this.getPreference('tts', 'openai-tts_api_key', '');
}
// OpenAI API URL
if (this.elements.openaiApiUrl) {
this.elements.openaiApiUrl.value = this.getPreference('tts', 'openai-tts_api_url', 'https://api.openai.com/v1');
}
// Audio Settings
// Master Volume
if (this.elements.masterVolume) {
const masterVolume = this.getPreference('audio', 'masterVolume', 1);
this.elements.masterVolume.value = Math.round(masterVolume * 100);
}
// Music Volume
if (this.elements.musicVolume) {
const musicVolume = this.getPreference('audio', 'musicVolume', 1);
this.elements.musicVolume.value = Math.round(musicVolume * 100);
}
// SFX Volume
if (this.elements.sfxVolume) {
const sfxVolume = this.getPreference('audio', 'sfxVolume', 1);
this.elements.sfxVolume.value = Math.round(sfxVolume * 100);
}
// Ambience Volume
if (this.elements.ambienceVolume) {
const ambienceVolume = this.getPreference('audio', 'ambienceVolume', 1);
this.elements.ambienceVolume.value = Math.round(ambienceVolume * 100);
}
// Language
if (this.elements.language) {
const locale = this.getPreference('app', 'locale', 'en');
if (this.elements.language.querySelector(`option[value="${locale}"]`)) {
this.elements.language.value = locale;
}
}
// Update API settings visibility
if (this.elements.ttsSystem) {
this.updateApiSettingsVisibility(this.elements.ttsSystem.value);
}
}
/**
* Set up two-way binding for TTS Enabled
* @param {HTMLElement} element - UI element
* @param {Object} persistenceManager - Persistence Manager module
* @param {string} category - Preference category
* @param {string} key - Preference key
* @param {*} defaultValue - Default value if preference doesn't exist
* @param {Function} [transform] - Optional transform function
*/
setupTtsEnabledBinding(element, persistenceManager, category, key, defaultValue, transform) {
createPreferenceBinding(
element,
persistenceManager,
category,
key,
defaultValue,
transform
);
}
/**
* Set up two-way binding for TTS Voice
* @param {HTMLElement} element - UI element
* @param {Object} persistenceManager - Persistence Manager module
* @param {string} category - Preference category
* @param {string} key - Preference key
* @param {*} defaultValue - Default value if preference doesn't exist
* @param {Function} [transform] - Optional transform function
*/
setupTtsVoiceBinding(element, persistenceManager, category, key, defaultValue, transform) {
createPreferenceBinding(
element,
persistenceManager,
category,
key,
defaultValue,
transform
);
}
/**
* Set up two-way binding for App Language
* @param {HTMLElement} element - UI element
* @param {Object} persistenceManager - Persistence Manager module
* @param {string} category - Preference category
* @param {string} key - Preference key
* @param {*} defaultValue - Default value if preference doesn't exist
* @param {Function} [transform] - Optional transform function
*/
setupLanguageBinding(element, persistenceManager, category, key, defaultValue, transform) {
createPreferenceBinding(
element,
persistenceManager,
category,
key,
defaultValue,
transform
);
}
/**
* Set up two-way binding for API settings
* @param {Object} persistenceManager - Persistence Manager module
*/
setupApiPreferenceBindings(persistenceManager) {
// ElevenLabs API Key
createPreferenceBinding(
this.elements.elevenLabsApiKey,
persistenceManager,
'tts',
'elevenlabs-tts_api_key',
null,
(value) => {
this.dispatchApiChangeEvent('api:keyChanged', 'elevenlabs-tts', 'key', value);
return value;
}
);
// ElevenLabs API URL
createPreferenceBinding(
this.elements.elevenLabsApiUrl,
persistenceManager,
'tts',
'elevenlabs-tts_api_url',
null,
(value) => {
this.dispatchApiChangeEvent('api:urlChanged', 'elevenlabs-tts', 'url', value);
return value;
}
);
// OpenAI API Key
createPreferenceBinding(
this.elements.openaiApiKey,
persistenceManager,
'tts',
'openai-tts_api_key',
null,
(value) => {
this.dispatchApiChangeEvent('api:keyChanged', 'openai-tts', 'key', value);
return value;
}
);
// OpenAI API URL
createPreferenceBinding(
this.elements.openaiApiUrl,
persistenceManager,
'tts',
'openai-tts_api_url',
null,
(value) => {
this.dispatchApiChangeEvent('api:urlChanged', 'openai-tts', 'url', value);
return value;
}
);
}
/**
* Save current settings
*/
saveCurrentSettings() {
// With two-way binding, settings are saved automatically as they change
console.log('Options UI: Settings saved');
}
/**
* Apply settings
*/
applySettings() {
const ttsFactory = this.getModule('tts-factory');
if (ttsFactory) {
// Apply TTS settings
const enabled = this.getPreference('tts', 'enabled', false);
const preferredHandler = this.getPreference('tts', 'preferred_handler', 'none');
ttsFactory.configure({ enabled });
ttsFactory.setActiveHandler(preferredHandler);
}
}
/**
* Show a reload notice
* @param {string} message - Message to show
*/
showReloadNotice(message) {
console.log('Options UI: Reload required -', message);
this.reloadRequired = true;
}
/**
* Set up listeners for settings that should save immediately
*/
setupImmediateSaveListeners() {
// Settings are saved immediately with two-way binding
}
/**
* Update UI text based on current language
*/
updateUIText() {
// Update UI text based on current language
const localization = this.getModule('localization');
if (!localization) return;
// Update modal title
const modalTitle = this.modal.querySelector('h2');
if (modalTitle) {
modalTitle.textContent = localization.translate('options.title', 'Options');
}
// Update section titles
const ttsSectionTitle = this.modal.querySelector('.options-section h3:first-child');
if (ttsSectionTitle) {
ttsSectionTitle.textContent = localization.translate('options.tts.title', 'Text-to-Speech');
}
const langSectionTitle = this.modal.querySelector('.options-section:nth-child(2) h3');
if (langSectionTitle) {
langSectionTitle.textContent = localization.translate('options.language.title', 'Language Settings');
}
}
/**
* Set up API URL fields with default values
*/
setupApiUrlFields() {
// Set up ElevenLabs API URL
if (this.elements.elevenLabsApiUrl) {
const savedUrl = this.getPreference('tts', 'elevenlabs-tts_api_url');
const defaultUrl = 'https://api.elevenlabs.io/v1';
// If no saved URL, set the default
if (!savedUrl) {
console.log('Options UI: Setting default ElevenLabs API URL:', defaultUrl);
this.updatePreference('tts', 'elevenlabs-tts_api_url', defaultUrl);
}
}
// Set up OpenAI API URL
if (this.elements.openaiApiUrl) {
const savedUrl = this.getPreference('tts', 'openai-tts_api_url');
const defaultUrl = 'https://api.openai.com/v1';
// If no saved URL, set the default
if (!savedUrl) {
console.log('Options UI: Setting default OpenAI API URL:', defaultUrl);
this.updatePreference('tts', 'openai-tts_api_url', defaultUrl);
}
}
// Make sure API keys are initialized if not already set
if (!this.getPreference('tts', 'elevenlabs-tts_api_key')) {
this.updatePreference('tts', 'elevenlabs-tts_api_key', '');
}
if (!this.getPreference('tts', 'openai-tts_api_key')) {
this.updatePreference('tts', 'openai-tts_api_key', '');
}
}
/**
* Set up the initial state of the Options UI
* @returns {Promise<boolean>} - Promise resolves when setup is complete
*/
async setupInitialState() {
try {
console.log('Options UI: Setting up initial state');
// Add event listener for toggling options UI
document.addEventListener('ui:options:toggle', () => this.toggle());
// Set up key bindings
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && this.modal && this.modal.style.display === 'flex') {
this.saveCurrentSettings();
this.hide();
}
});
// Populate TTS systems
await this.populateTtsSystems();
// Populate languages
await this.populateLanguages();
// Populate voices based on current TTS system
await this.populateVoices();
// Load current preferences
this.loadPreferences();
// Register for TTS events to update voices when they change
document.addEventListener('tts:voices:updated', () => {
console.log('Options UI: Received tts:voices:updated event, updating voice dropdown');
this.populateVoices();
});
// Set up language change listener
document.addEventListener('locale:changed', async () => {
this.updateUIText();
await this.populateLanguages();
});
// Register event listeners for TTS availability and voiceId changes
document.addEventListener('tts:engine:change', async (event) => {
console.log('Options UI: Received TTS engine change event:', event.detail);
await this.populateVoices();
this.updateApiSettingsVisibility(this.elements.ttsSystem.value);
});
console.log('Options UI: Initial state setup complete');
return true;
} catch (error) {
console.error('Options UI: Error setting up initial state', error);
return false;
}
}
}
// Create the singleton instance
const OptionsUI = new OptionsUIModule();
// Register with the module registry
moduleRegistry.register(OptionsUI);
// Export the module
export { OptionsUI };