Split everything up into dynamically loaded modules.

This commit is contained in:
2025-04-04 00:00:43 +00:00
parent 2f7cda4b6d
commit aa29a6fd93
32 changed files with 8768 additions and 3935 deletions
+947
View File
@@ -0,0 +1,947 @@
/**
* Options UI Module for AI Interactive Fiction
* Provides a user interface for adjusting game settings, TTS options, etc.
*/
import { BaseModule } from './base-module.js';
import { moduleRegistry } from './module-registry.js';
class OptionsUIModule extends BaseModule {
/**
* Create new options UI
*/
constructor() {
super('options-ui', 'Options UI');
this.persistenceManager = null;
this.ttsPlayer = null;
this.audioManager = null;
this.ttsFactory = null;
this.modal = null;
this.isOpen = false;
// Configuration
this.config = {
modalClass: 'options-modal',
modalContentClass: 'options-content',
backdrop: true
};
// Bound event handlers for proper this context
this.handleTtsSystemChanged = this.handleTtsSystemChanged.bind(this);
}
/**
* Initialize the module
* @returns {Promise<boolean>} - Resolves with success status
*/
async initialize() {
try {
// Set up event listeners
window.addEventListener('tts-system-changed', this.handleTtsSystemChanged);
// The option modal will be created on demand
this.reportProgress(100, "Options UI ready");
return true;
} catch (error) {
console.error("Error initializing options UI:", error);
return false;
}
}
/**
* Handle TTS system changes
* @param {CustomEvent} event - The event containing TTS system change details
*/
handleTtsSystemChanged(event) {
console.log("TTS system changed:", event.detail);
if (this.isOpen) {
// Refresh the voices list if the options UI is currently open
this.populateVoices();
}
}
/**
* Wait for dependencies to be ready
* @returns {Promise<boolean>} - Resolves when dependencies are ready
*/
async waitForDependencies() {
try {
// Wait for the persistence manager if available
this.persistenceManager = moduleRegistry.getModule('persistence-manager');
this.ttsPlayer = moduleRegistry.getModule('tts');
// These dependencies are optional - UI will adapt if not available
this.audioManager = moduleRegistry.getModule('audio-manager');
return true;
} catch (error) {
console.error("Error waiting for options UI dependencies:", error);
return true; // Non-critical, can continue
}
}
/**
* Create the options UI elements
*/
createModal() {
if (this.modal) return;
// Create modal container
this.modal = document.createElement('div');
this.modal.className = this.config.modalClass;
this.modal.style.display = 'none';
// Create backdrop if enabled
if (this.config.backdrop) {
this.backdrop = document.createElement('div');
this.backdrop.className = 'modal-backdrop';
this.backdrop.addEventListener('click', () => this.hide());
this.modal.appendChild(this.backdrop);
}
// Create content container
const content = document.createElement('div');
content.className = this.config.modalContentClass;
// Add header with title and close button
const header = document.createElement('div');
header.className = 'options-header';
const title = document.createElement('h2');
title.textContent = 'Options';
header.appendChild(title);
const closeBtn = document.createElement('button');
closeBtn.className = 'close-button';
closeBtn.textContent = '×';
closeBtn.setAttribute('aria-label', 'Close options');
closeBtn.addEventListener('click', () => this.hide());
header.appendChild(closeBtn);
content.appendChild(header);
// Create tabs
const tabContainer = document.createElement('div');
tabContainer.className = 'tabs-container';
const tabs = document.createElement('div');
tabs.className = 'tabs';
const tabGeneral = document.createElement('button');
tabGeneral.className = 'tab active';
tabGeneral.textContent = 'General';
tabGeneral.dataset.tab = 'general';
const tabVoice = document.createElement('button');
tabVoice.className = 'tab';
tabVoice.textContent = 'Voice';
tabVoice.dataset.tab = 'voice';
const tabAudio = document.createElement('button');
tabAudio.className = 'tab';
tabAudio.textContent = 'Audio';
tabAudio.dataset.tab = 'audio';
const tabAccess = document.createElement('button');
tabAccess.className = 'tab';
tabAccess.textContent = 'Accessibility';
tabAccess.dataset.tab = 'accessibility';
tabs.appendChild(tabGeneral);
tabs.appendChild(tabVoice);
tabs.appendChild(tabAudio);
tabs.appendChild(tabAccess);
tabContainer.appendChild(tabs);
content.appendChild(tabContainer);
// Create tab content sections
const tabContent = document.createElement('div');
tabContent.className = 'tab-content';
// General tab content
const generalContent = document.createElement('div');
generalContent.className = 'tab-pane active';
generalContent.dataset.tab = 'general';
const animSpeedSection = document.createElement('div');
animSpeedSection.className = 'option-section';
const animSpeedLabel = document.createElement('label');
animSpeedLabel.textContent = 'Animation Speed';
animSpeedLabel.htmlFor = 'option-anim-speed';
const animSpeedSlider = document.createElement('input');
animSpeedSlider.type = 'range';
animSpeedSlider.id = 'option-anim-speed';
animSpeedSlider.min = '0';
animSpeedSlider.max = '100';
animSpeedSlider.value = '50'; // Will be updated from preferences
const animSpeedValue = document.createElement('span');
animSpeedValue.className = 'range-value';
animSpeedValue.textContent = '50%';
animSpeedSlider.addEventListener('input', () => {
const val = animSpeedSlider.value;
animSpeedValue.textContent = `${val}%`;
if (this.persistenceManager) {
this.persistenceManager.updatePreference('animation', 'speed', parseInt(val, 10));
}
// Update animation queue speed if available
const animQueue = moduleRegistry.getModule('animation-queue');
if (animQueue) {
const speed = Math.pow(100.0 - val, 3) / 10000 * 10 + 0.01;
animQueue.setSpeed(speed);
}
});
animSpeedSection.appendChild(animSpeedLabel);
animSpeedSection.appendChild(animSpeedSlider);
animSpeedSection.appendChild(animSpeedValue);
generalContent.appendChild(animSpeedSection);
// Voice tab content
const voiceContent = document.createElement('div');
voiceContent.className = 'tab-pane';
voiceContent.dataset.tab = 'voice';
const ttsSysSection = document.createElement('div');
ttsSysSection.className = 'option-section';
const ttsSysLabel = document.createElement('label');
ttsSysLabel.textContent = 'TTS System';
ttsSysLabel.htmlFor = 'option-tts-system';
const ttsSysSelect = document.createElement('select');
ttsSysSelect.id = 'option-tts-system';
// Will populate systems dynamically later
ttsSysSection.appendChild(ttsSysLabel);
ttsSysSection.appendChild(ttsSysSelect);
voiceContent.appendChild(ttsSysSection);
// Voice selection section
const voiceSection = document.createElement('div');
voiceSection.className = 'option-section';
const voiceLabel = document.createElement('label');
voiceLabel.textContent = 'Voice';
voiceLabel.htmlFor = 'option-voice';
const voiceSelect = document.createElement('select');
voiceSelect.id = 'option-voice';
// Will populate voices dynamically later
voiceSection.appendChild(voiceLabel);
voiceSection.appendChild(voiceSelect);
voiceContent.appendChild(voiceSection);
// Voice rate section
const rateSection = document.createElement('div');
rateSection.className = 'option-section';
const rateLabel = document.createElement('label');
rateLabel.textContent = 'Speech Rate';
rateLabel.htmlFor = 'option-speech-rate';
const rateSlider = document.createElement('input');
rateSlider.type = 'range';
rateSlider.id = 'option-speech-rate';
rateSlider.min = '50';
rateSlider.max = '200';
rateSlider.value = '100'; // Will be updated from preferences
const rateValue = document.createElement('span');
rateValue.className = 'range-value';
rateValue.textContent = '1.0x';
rateSlider.addEventListener('input', () => {
const val = rateSlider.value;
const rate = val / 100;
rateValue.textContent = `${rate.toFixed(1)}x`;
if (this.ttsPlayer) {
this.ttsPlayer.setSpeed(rate);
}
if (this.persistenceManager) {
this.persistenceManager.updatePreference('tts', 'rate', rate);
}
});
rateSection.appendChild(rateLabel);
rateSection.appendChild(rateSlider);
rateSection.appendChild(rateValue);
voiceContent.appendChild(rateSection);
// Audio tab content
const audioContent = document.createElement('div');
audioContent.className = 'tab-pane';
audioContent.dataset.tab = 'audio';
// Master volume section
const masterVolSection = document.createElement('div');
masterVolSection.className = 'option-section';
const masterVolLabel = document.createElement('label');
masterVolLabel.textContent = 'Master Volume';
masterVolLabel.htmlFor = 'option-master-vol';
const masterVolSlider = document.createElement('input');
masterVolSlider.type = 'range';
masterVolSlider.id = 'option-master-vol';
masterVolSlider.min = '0';
masterVolSlider.max = '100';
masterVolSlider.value = '100'; // Will be updated from preferences
const masterVolValue = document.createElement('span');
masterVolValue.className = 'range-value';
masterVolValue.textContent = '100%';
masterVolSlider.addEventListener('input', () => {
const val = masterVolSlider.value;
masterVolValue.textContent = `${val}%`;
if (this.audioManager) {
this.audioManager.setMasterVolume(val / 100);
}
if (this.persistenceManager) {
this.persistenceManager.updatePreference('audio', 'masterVolume', val / 100);
}
});
masterVolSection.appendChild(masterVolLabel);
masterVolSection.appendChild(masterVolSlider);
masterVolSection.appendChild(masterVolValue);
audioContent.appendChild(masterVolSection);
// TTS volume section
const ttsVolSection = document.createElement('div');
ttsVolSection.className = 'option-section';
const ttsVolLabel = document.createElement('label');
ttsVolLabel.textContent = 'Speech Volume';
ttsVolLabel.htmlFor = 'option-tts-vol';
const ttsVolSlider = document.createElement('input');
ttsVolSlider.type = 'range';
ttsVolSlider.id = 'option-tts-vol';
ttsVolSlider.min = '0';
ttsVolSlider.max = '100';
ttsVolSlider.value = '100'; // Will be updated from preferences
const ttsVolValue = document.createElement('span');
ttsVolValue.className = 'range-value';
ttsVolValue.textContent = '100%';
ttsVolSlider.addEventListener('input', () => {
const val = ttsVolSlider.value;
ttsVolValue.textContent = `${val}%`;
if (this.ttsPlayer) {
this.ttsPlayer.setVolume(val / 100);
}
if (this.persistenceManager) {
this.persistenceManager.updatePreference('tts', 'volume', val / 100);
}
});
ttsVolSection.appendChild(ttsVolLabel);
ttsVolSection.appendChild(ttsVolSlider);
ttsVolSection.appendChild(ttsVolValue);
audioContent.appendChild(ttsVolSection);
// Music volume section (for future use)
const musicVolSection = document.createElement('div');
musicVolSection.className = 'option-section';
const musicVolLabel = document.createElement('label');
musicVolLabel.textContent = 'Music Volume';
musicVolLabel.htmlFor = 'option-music-vol';
const musicVolSlider = document.createElement('input');
musicVolSlider.type = 'range';
musicVolSlider.id = 'option-music-vol';
musicVolSlider.min = '0';
musicVolSlider.max = '100';
musicVolSlider.value = '70'; // Will be updated from preferences
const musicVolValue = document.createElement('span');
musicVolValue.className = 'range-value';
musicVolValue.textContent = '70%';
musicVolSlider.addEventListener('input', () => {
const val = musicVolSlider.value;
musicVolValue.textContent = `${val}%`;
if (this.audioManager) {
this.audioManager.setMusicVolume(val / 100);
}
if (this.persistenceManager) {
this.persistenceManager.updatePreference('audio', 'musicVolume', val / 100);
}
});
musicVolSection.appendChild(musicVolLabel);
musicVolSection.appendChild(musicVolSlider);
musicVolSection.appendChild(musicVolValue);
audioContent.appendChild(musicVolSection);
// SFX volume section (for future use)
const sfxVolSection = document.createElement('div');
sfxVolSection.className = 'option-section';
const sfxVolLabel = document.createElement('label');
sfxVolLabel.textContent = 'Effects Volume';
sfxVolLabel.htmlFor = 'option-sfx-vol';
const sfxVolSlider = document.createElement('input');
sfxVolSlider.type = 'range';
sfxVolSlider.id = 'option-sfx-vol';
sfxVolSlider.min = '0';
sfxVolSlider.max = '100';
sfxVolSlider.value = '100'; // Will be updated from preferences
const sfxVolValue = document.createElement('span');
sfxVolValue.className = 'range-value';
sfxVolValue.textContent = '100%';
sfxVolSlider.addEventListener('input', () => {
const val = sfxVolSlider.value;
sfxVolValue.textContent = `${val}%`;
if (this.audioManager) {
this.audioManager.setSfxVolume(val / 100);
}
if (this.persistenceManager) {
this.persistenceManager.updatePreference('audio', 'sfxVolume', val / 100);
}
});
sfxVolSection.appendChild(sfxVolLabel);
sfxVolSection.appendChild(sfxVolSlider);
sfxVolSection.appendChild(sfxVolValue);
audioContent.appendChild(sfxVolSection);
// Accessibility tab content
const accessContent = document.createElement('div');
accessContent.className = 'tab-pane';
accessContent.dataset.tab = 'accessibility';
// High contrast toggle
const contrastSection = document.createElement('div');
contrastSection.className = 'option-section checkbox-section';
const contrastCheckbox = document.createElement('input');
contrastCheckbox.type = 'checkbox';
contrastCheckbox.id = 'option-high-contrast';
const contrastLabel = document.createElement('label');
contrastLabel.textContent = 'High Contrast Mode';
contrastLabel.htmlFor = 'option-high-contrast';
contrastCheckbox.addEventListener('change', () => {
const isEnabled = contrastCheckbox.checked;
// Apply high contrast class to body
if (isEnabled) {
document.body.classList.add('high-contrast');
} else {
document.body.classList.remove('high-contrast');
}
if (this.persistenceManager) {
this.persistenceManager.updatePreference('accessibility', 'highContrast', isEnabled);
}
});
contrastSection.appendChild(contrastCheckbox);
contrastSection.appendChild(contrastLabel);
accessContent.appendChild(contrastSection);
// Larger text toggle
const largerTextSection = document.createElement('div');
largerTextSection.className = 'option-section checkbox-section';
const largerTextCheckbox = document.createElement('input');
largerTextCheckbox.type = 'checkbox';
largerTextCheckbox.id = 'option-larger-text';
const largerTextLabel = document.createElement('label');
largerTextLabel.textContent = 'Larger Text';
largerTextLabel.htmlFor = 'option-larger-text';
largerTextCheckbox.addEventListener('change', () => {
const isEnabled = largerTextCheckbox.checked;
// Apply larger text class to body
if (isEnabled) {
document.body.classList.add('larger-text');
} else {
document.body.classList.remove('larger-text');
}
if (this.persistenceManager) {
this.persistenceManager.updatePreference('accessibility', 'largerText', isEnabled);
}
});
largerTextSection.appendChild(largerTextCheckbox);
largerTextSection.appendChild(largerTextLabel);
accessContent.appendChild(largerTextSection);
// Add tab content to container
tabContent.appendChild(generalContent);
tabContent.appendChild(voiceContent);
tabContent.appendChild(audioContent);
tabContent.appendChild(accessContent);
content.appendChild(tabContent);
// Add buttons at the bottom
const buttons = document.createElement('div');
buttons.className = 'options-buttons';
const resetButton = document.createElement('button');
resetButton.textContent = 'Reset to Defaults';
resetButton.className = 'reset-button';
resetButton.addEventListener('click', () => this.resetToDefaults());
const saveButton = document.createElement('button');
saveButton.textContent = 'Save & Close';
saveButton.className = 'save-button';
saveButton.addEventListener('click', () => this.saveAndClose());
buttons.appendChild(resetButton);
buttons.appendChild(saveButton);
content.appendChild(buttons);
// Set up tab switching
tabs.addEventListener('click', (e) => {
if (e.target.classList.contains('tab')) {
// Deactivate all tabs and tab panes
Array.from(tabs.querySelectorAll('.tab')).forEach(tab => {
tab.classList.remove('active');
});
Array.from(tabContent.querySelectorAll('.tab-pane')).forEach(pane => {
pane.classList.remove('active');
});
// Activate clicked tab and corresponding pane
e.target.classList.add('active');
const tabName = e.target.dataset.tab;
const pane = tabContent.querySelector(`.tab-pane[data-tab="${tabName}"]`);
if (pane) {
pane.classList.add('active');
}
// If switching to voice tab, ensure voices are updated
if (tabName === 'voice') {
this.populateTtsSystems();
this.populateVoices();
}
}
});
this.modal.appendChild(content);
document.body.appendChild(this.modal);
// Store references to UI elements for later use
this.elements = {
animSpeed: animSpeedSlider,
animSpeedValue: animSpeedValue,
ttsSystem: ttsSysSelect,
voiceSelect: voiceSelect,
speechRate: rateSlider,
speechRateValue: rateValue,
masterVolume: masterVolSlider,
masterVolumeValue: masterVolValue,
ttsVolume: ttsVolSlider,
ttsVolumeValue: ttsVolValue,
musicVolume: musicVolSlider,
musicVolumeValue: musicVolValue,
sfxVolume: sfxVolSlider,
sfxVolumeValue: sfxVolValue,
highContrast: contrastCheckbox,
largerText: largerTextCheckbox
};
}
/**
* Show the options UI
*/
show() {
if (!this.modal) {
this.createModal();
}
// Load current preferences
this.loadPreferences();
// Populate TTS systems and voices
this.populateTtsSystems();
this.populateVoices();
// Show the modal
this.modal.style.display = 'flex';
this.isOpen = true;
}
/**
* Hide the options UI
*/
hide() {
if (this.modal) {
this.modal.style.display = 'none';
this.isOpen = false;
}
}
/**
* Toggle the options UI visibility
*/
toggle() {
if (this.isOpen) {
this.hide();
} else {
this.show();
}
}
/**
* Load current preferences into UI
*/
loadPreferences() {
if (!this.persistenceManager || !this.elements) return;
const prefs = this.persistenceManager.getAllPreferences();
// Animation speed
const animSpeed = this.persistenceManager.getPreference('animation', 'speed', 50);
this.elements.animSpeed.value = animSpeed;
this.elements.animSpeedValue.textContent = `${animSpeed}%`;
// TTS settings
const ttsEnabled = this.persistenceManager.getPreference('tts', 'enabled', false);
const ttsProvider = this.persistenceManager.getPreference('tts', 'provider', 'browser');
const ttsVoice = this.persistenceManager.getPreference('tts', 'voice', '');
const ttsVolume = this.persistenceManager.getPreference('tts', 'volume', 1.0);
const ttsRate = this.persistenceManager.getPreference('tts', 'rate', 1.0);
// TTS rate slider
this.elements.speechRate.value = Math.round(ttsRate * 100);
this.elements.speechRateValue.textContent = `${ttsRate.toFixed(1)}x`;
// TTS volume slider
this.elements.ttsVolume.value = Math.round(ttsVolume * 100);
this.elements.ttsVolumeValue.textContent = `${Math.round(ttsVolume * 100)}%`;
// Audio volumes
const masterVolume = this.persistenceManager.getPreference('audio', 'masterVolume', 1.0);
const musicVolume = this.persistenceManager.getPreference('audio', 'musicVolume', 0.7);
const sfxVolume = this.persistenceManager.getPreference('audio', 'sfxVolume', 1.0);
this.elements.masterVolume.value = Math.round(masterVolume * 100);
this.elements.masterVolumeValue.textContent = `${Math.round(masterVolume * 100)}%`;
this.elements.musicVolume.value = Math.round(musicVolume * 100);
this.elements.musicVolumeValue.textContent = `${Math.round(musicVolume * 100)}%`;
this.elements.sfxVolume.value = Math.round(sfxVolume * 100);
this.elements.sfxVolumeValue.textContent = `${Math.round(sfxVolume * 100)}%`;
// Accessibility settings
const highContrast = this.persistenceManager.getPreference('accessibility', 'highContrast', false);
const largerText = this.persistenceManager.getPreference('accessibility', 'largerText', false);
this.elements.highContrast.checked = highContrast;
this.elements.largerText.checked = largerText;
}
/**
* Populate TTS systems dropdown
*/
populateTtsSystems() {
if (!this.ttsPlayer || !this.elements) return;
const systems = this.ttsPlayer.getAvailableSystems();
const select = this.elements.ttsSystem;
// Clear existing options and listeners
select.innerHTML = '';
const newSelect = select.cloneNode(false);
select.parentNode.replaceChild(newSelect, select);
this.elements.ttsSystem = newSelect;
select = newSelect;
// Get current TTS info
const currentInfo = this.ttsPlayer.getTTSInfo();
const currentId = currentInfo.type || '';
// Create an option for each available system
systems.forEach(id => {
const option = document.createElement('option');
option.value = id;
switch (id) {
case 'browser':
option.textContent = 'Browser Built-in TTS';
break;
case 'kokoro':
option.textContent = 'Kokoro Neural TTS';
break;
case 'api':
option.textContent = 'API-based TTS';
break;
default:
option.textContent = id.charAt(0).toUpperCase() + id.slice(1);
}
if (id === currentId) {
option.selected = true;
}
select.appendChild(option);
});
// Add change listener
select.addEventListener('change', () => {
const selectedSystem = select.value;
if (this.ttsPlayer) {
this.ttsPlayer.switchTTS(selectedSystem);
// Update persistence
if (this.persistenceManager) {
this.persistenceManager.updatePreference('tts', 'provider', selectedSystem);
}
}
});
}
/**
* Populate voices dropdown for current TTS system
*/
async populateVoices() {
if (!this.ttsPlayer || !this.elements || !this.ttsPlayer.getVoices) return;
try {
const voices = await this.ttsPlayer.getVoices();
const select = this.elements.voiceSelect;
// Clear existing options and listeners
select.innerHTML = '';
const newSelect = select.cloneNode(false);
select.parentNode.replaceChild(newSelect, select);
this.elements.voiceSelect = newSelect;
select = newSelect;
if (!voices || voices.length === 0) {
const option = document.createElement('option');
option.value = '';
option.textContent = 'No voices available';
select.appendChild(option);
select.disabled = true;
return;
}
select.disabled = false;
// Get current preference
let currentVoice = '';
if (this.persistenceManager) {
currentVoice = this.persistenceManager.getPreference('tts', 'voice', '');
}
// Add voices to dropdown
voices.forEach(voice => {
const option = document.createElement('option');
option.value = voice.id || voice.name;
option.textContent = voice.name;
if (voice.id === currentVoice || voice.name === currentVoice) {
option.selected = true;
}
select.appendChild(option);
});
// Add change listener
select.addEventListener('change', () => {
const selectedVoice = select.value;
// Update TTS
if (this.ttsPlayer) {
this.ttsPlayer.setVoice(selectedVoice);
}
// Update persistence
if (this.persistenceManager) {
this.persistenceManager.updatePreference('tts', 'voice', selectedVoice);
}
});
console.log(`Voices populated for current TTS system. Selected: ${select.value}`);
} catch (error) {
console.error("Error populating voices:", error);
const select = this.elements.voiceSelect;
select.innerHTML = '';
const option = document.createElement('option');
option.value = '';
option.textContent = 'Error loading voices';
select.appendChild(option);
select.disabled = true;
}
}
/**
* Reset all options to defaults
*/
resetToDefaults() {
if (!this.persistenceManager) return;
const confirmed = confirm('Reset all options to default values?');
if (confirmed) {
// Reset preferences
this.persistenceManager.resetPreferences();
// Update UI
this.loadPreferences();
// Apply changes
this.applySettings();
// Refresh voice list
this.populateVoices();
}
}
/**
* Save settings and close modal
*/
saveAndClose() {
if (this.persistenceManager && this.elements) {
// Save preferences - already saved as they change
// Apply settings
this.applySettings();
}
this.hide();
}
/**
* Apply current settings to the app
*/
applySettings() {
if (!this.persistenceManager) return;
// Apply animation speed
const animSpeed = this.persistenceManager.getPreference('animation', 'speed', 50);
const animQueue = moduleRegistry.getModule('animation-queue');
if (animQueue) {
const speed = Math.pow(100.0 - animSpeed, 3) / 10000 * 10 + 0.01;
animQueue.setSpeed(speed);
}
// Apply TTS settings
const ttsEnabled = this.persistenceManager.getPreference('tts', 'enabled', false);
const ttsProvider = this.persistenceManager.getPreference('tts', 'provider', 'browser');
const ttsVoice = this.persistenceManager.getPreference('tts', 'voice', '');
const ttsVolume = this.persistenceManager.getPreference('tts', 'volume', 1.0);
const ttsRate = this.persistenceManager.getPreference('tts', 'rate', 1.0);
if (this.ttsPlayer) {
// Set TTS system
if (ttsProvider) {
this.ttsPlayer.switchTTS(ttsProvider);
}
// Apply voice options
this.ttsPlayer.setVoiceOptions({
voice: ttsVoice,
volume: ttsVolume,
rate: ttsRate
});
}
// Apply audio volume settings
const masterVolume = this.persistenceManager.getPreference('audio', 'masterVolume', 1.0);
const musicVolume = this.persistenceManager.getPreference('audio', 'musicVolume', 0.7);
const sfxVolume = this.persistenceManager.getPreference('audio', 'sfxVolume', 1.0);
if (this.audioManager) {
this.audioManager.setMasterVolume(masterVolume);
this.audioManager.setMusicVolume(musicVolume);
this.audioManager.setSfxVolume(sfxVolume);
}
// Apply accessibility settings
const highContrast = this.persistenceManager.getPreference('accessibility', 'highContrast', false);
const largerText = this.persistenceManager.getPreference('accessibility', 'largerText', false);
if (highContrast) {
document.body.classList.add('high-contrast');
} else {
document.body.classList.remove('high-contrast');
}
if (largerText) {
document.body.classList.add('larger-text');
} else {
document.body.classList.remove('larger-text');
}
}
/**
* Set the TTS factory reference
* @param {Object} factory - The TTS factory instance
*/
setTtsFactory(factory) {
this.ttsFactory = factory;
}
/**
* Update available TTS systems info
* @param {Object} systemsInfo - Information about available TTS systems
*/
updateAvailableSystems(systemsInfo) {
// Will repopulate next time UI is opened
console.log("TTS systems info updated:", systemsInfo);
// If the options UI is currently open, update it
if (this.isOpen) {
this.populateTtsSystems();
this.populateVoices();
}
}
/**
* Clean up when module is disposed
*/
dispose() {
// Remove event listeners
window.removeEventListener('tts-system-changed', this.handleTtsSystemChanged);
}
}
// Create the singleton instance
const OptionsUI = new OptionsUIModule();
// Register with the module registry
moduleRegistry.register(OptionsUI);
// Export the module
export { OptionsUI };
// Keep a reference in window for loader system
window.OptionsUI = OptionsUI;