Cleaned persistence manager, updated ui-options connectivity

This commit is contained in:
2025-04-06 19:35:05 +00:00
parent 0ab639fd25
commit 0842cbfefc
6 changed files with 107 additions and 114 deletions
+16
View File
@@ -188,6 +188,22 @@ export class BaseModule {
return this.state; return this.state;
} }
/**
* Get the module ID
* @returns {string} - Module ID
*/
getId() {
return this.id;
}
/**
* Get the module name
* @returns {string} - Module name
*/
getName() {
return this.name;
}
/** /**
* Dispatch a module event * Dispatch a module event
* @param {string} name - Event name * @param {string} name - Event name
+7 -7
View File
@@ -38,9 +38,9 @@ export class ElevenLabsTTSModule extends ApiTTSModuleBase {
return false; return false;
} }
// Check for API key // API key is already loaded in parent initialize() method
const apiKey = persistenceManager.getPreference('elevenlabs', 'api_key', ''); // Just check if it's available
if (!apiKey) { if (!this.apiKey) {
console.error('ElevenLabs TTS: API key not configured'); console.error('ElevenLabs TTS: API key not configured');
return false; return false;
} }
@@ -48,24 +48,24 @@ export class ElevenLabsTTSModule extends ApiTTSModuleBase {
// Load voices from ElevenLabs // Load voices from ElevenLabs
try { try {
this.reportProgress(50, 'Loading ElevenLabs voices'); this.reportProgress(50, 'Loading ElevenLabs voices');
await this.loadVoices(apiKey); await this.loadVoices(this.apiKey);
} catch (error) { } catch (error) {
console.error('ElevenLabs TTS: Failed to load voices:', error); console.error('ElevenLabs TTS: Failed to load voices:', error);
return false; return false;
} }
// Load preferences // Load preferences
const preferredVoice = persistenceManager.getPreference('elevenlabs', 'voice', this.voiceOptions.voice); const preferredVoice = persistenceManager.getPreference('tts', `${this.id}_voice`, this.voiceOptions.voice);
if (preferredVoice) { if (preferredVoice) {
this.voiceOptions.voice = preferredVoice; this.voiceOptions.voice = preferredVoice;
} }
const preferredModel = persistenceManager.getPreference('elevenlabs', 'model', this.voiceOptions.model); const preferredModel = persistenceManager.getPreference('tts', `${this.id}_model`, this.voiceOptions.model);
if (preferredModel) { if (preferredModel) {
this.voiceOptions.model = preferredModel; this.voiceOptions.model = preferredModel;
} }
const preferredSpeed = persistenceManager.getPreference('elevenlabs', 'speed', this.voiceOptions.speed); const preferredSpeed = persistenceManager.getPreference('tts', `${this.id}_speed`, this.voiceOptions.speed);
if (typeof preferredSpeed === 'number') { if (typeof preferredSpeed === 'number') {
this.voiceOptions.speed = preferredSpeed; this.voiceOptions.speed = preferredSpeed;
} }
+9 -12
View File
@@ -57,28 +57,25 @@ export class OpenAITTSModule extends ApiTTSModuleBase {
return false; return false;
} }
// Check for API key // API key is already loaded in parent initialize() method
const apiKey = persistenceManager.getPreference('openai', 'api_key', ''); // Just check if it's available
if (!apiKey) { if (!this.apiKey) {
console.error('OpenAI TTS: API key not configured'); console.error('OpenAI TTS: API key not configured');
return false; return false;
} }
// Set API key
this.apiKey = apiKey;
// Load preferences // Load preferences
const preferredVoice = persistenceManager.getPreference('openai', 'voice', this.voiceOptions.voice); const preferredVoice = persistenceManager.getPreference('tts', `${this.id}_voice`, this.voiceOptions.voice);
if (preferredVoice) { if (preferredVoice) {
this.voiceOptions.voice = preferredVoice; this.voiceOptions.voice = preferredVoice;
} }
const preferredModel = persistenceManager.getPreference('openai', 'model', this.voiceOptions.model); const preferredModel = persistenceManager.getPreference('tts', `${this.id}_model`, this.voiceOptions.model);
if (preferredModel) { if (preferredModel) {
this.voiceOptions.model = preferredModel; this.voiceOptions.model = preferredModel;
} }
const preferredSpeed = persistenceManager.getPreference('openai', 'speed', this.voiceOptions.speed); const preferredSpeed = persistenceManager.getPreference('tts', `${this.id}_speed`, this.voiceOptions.speed);
if (typeof preferredSpeed === 'number') { if (typeof preferredSpeed === 'number') {
this.voiceOptions.speed = preferredSpeed; this.voiceOptions.speed = preferredSpeed;
} }
@@ -206,7 +203,7 @@ export class OpenAITTSModule extends ApiTTSModuleBase {
// Save voice preference // Save voice preference
const persistenceManager = this.getModule('persistence-manager'); const persistenceManager = this.getModule('persistence-manager');
if (persistenceManager) { if (persistenceManager) {
persistenceManager.updatePreference('tts', 'openai_voice', options.voice); persistenceManager.updatePreference('tts', `${this.id}_voice`, options.voice);
} }
} }
@@ -221,7 +218,7 @@ export class OpenAITTSModule extends ApiTTSModuleBase {
// Save the model preference // Save the model preference
const persistenceManager = this.getModule('persistence-manager'); const persistenceManager = this.getModule('persistence-manager');
if (persistenceManager) { if (persistenceManager) {
persistenceManager.updatePreference('tts', 'openai_model', options.model); persistenceManager.updatePreference('tts', `${this.id}_model`, options.model);
} }
} }
@@ -234,7 +231,7 @@ export class OpenAITTSModule extends ApiTTSModuleBase {
// Save the format preference // Save the format preference
const persistenceManager = this.getModule('persistence-manager'); const persistenceManager = this.getModule('persistence-manager');
if (persistenceManager) { if (persistenceManager) {
persistenceManager.updatePreference('tts', 'openai_format', options.response_format); persistenceManager.updatePreference('tts', `${this.id}_format`, options.response_format);
} }
} }
} }
+63 -74
View File
@@ -60,7 +60,9 @@ class OptionsUIModule extends BaseModule {
* @param {string} value - Value to dispatch * @param {string} value - Value to dispatch
*/ */
dispatchApiChangeEvent(eventType, provider, valueType, value) { dispatchApiChangeEvent(eventType, provider, valueType, value) {
document.dispatchEvent(new CustomEvent(eventType, { const eventName = `tts:${eventType}`;
console.log(`Options UI: Dispatching event ${eventName} for provider ${provider}`);
document.dispatchEvent(new CustomEvent(eventName, {
detail: { provider, [valueType]: value } detail: { provider, [valueType]: value }
})); }));
} }
@@ -135,7 +137,16 @@ class OptionsUIModule extends BaseModule {
// Create settings container // Create settings container
const settings = createUIElement('div', { className: 'options-settings' }, null, modalContent); 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 // TTS Settings
const ttsSection = createUIElement('div', { className: 'options-section' }, null, settings); const ttsSection = createUIElement('div', { className: 'options-section' }, null, settings);
createUIElement('h3', {}, 'Text-to-Speech', ttsSection); createUIElement('h3', {}, 'Text-to-Speech', ttsSection);
@@ -212,15 +223,6 @@ class OptionsUIModule extends BaseModule {
max: '100' max: '100'
}, null, ambienceVolumeContainer); }, null, ambienceVolumeContainer);
// 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);
// Initialize with display: none // Initialize with display: none
this.modal.style.display = 'none'; this.modal.style.display = 'none';
@@ -240,8 +242,8 @@ class OptionsUIModule extends BaseModule {
// ElevenLabs settings // ElevenLabs settings
// API Key // API Key
const elevenLabsApiKeyContainer = createUIElement('div', { const elevenLabsApiKeyContainer = createUIElement('div', {
className: 'options-row elevenlabs-setting', className: 'options-row elevenlabs-tts-setting',
'data-provider': 'elevenlabs' 'data-provider': 'elevenlabs-tts'
}, null, parentSection); }, null, parentSection);
createUIElement('label', {}, 'ElevenLabs API Key:', elevenLabsApiKeyContainer); createUIElement('label', {}, 'ElevenLabs API Key:', elevenLabsApiKeyContainer);
@@ -252,8 +254,8 @@ class OptionsUIModule extends BaseModule {
// API URL // API URL
const elevenLabsApiUrlContainer = createUIElement('div', { const elevenLabsApiUrlContainer = createUIElement('div', {
className: 'options-row elevenlabs-setting', className: 'options-row elevenlabs-tts-setting',
'data-provider': 'elevenlabs' 'data-provider': 'elevenlabs-tts'
}, null, parentSection); }, null, parentSection);
createUIElement('label', {}, 'ElevenLabs API URL:', elevenLabsApiUrlContainer); createUIElement('label', {}, 'ElevenLabs API URL:', elevenLabsApiUrlContainer);
@@ -265,8 +267,8 @@ class OptionsUIModule extends BaseModule {
// OpenAI settings // OpenAI settings
// API Key // API Key
const openaiApiKeyContainer = createUIElement('div', { const openaiApiKeyContainer = createUIElement('div', {
className: 'options-row openai-setting', className: 'options-row openai-tts-setting',
'data-provider': 'openai' 'data-provider': 'openai-tts'
}, null, parentSection); }, null, parentSection);
createUIElement('label', {}, 'OpenAI API Key:', openaiApiKeyContainer); createUIElement('label', {}, 'OpenAI API Key:', openaiApiKeyContainer);
@@ -277,8 +279,8 @@ class OptionsUIModule extends BaseModule {
// API URL // API URL
const openaiApiUrlContainer = createUIElement('div', { const openaiApiUrlContainer = createUIElement('div', {
className: 'options-row openai-setting', className: 'options-row openai-tts-setting',
'data-provider': 'openai' 'data-provider': 'openai-tts'
}, null, parentSection); }, null, parentSection);
createUIElement('label', {}, 'OpenAI API URL:', openaiApiUrlContainer); createUIElement('label', {}, 'OpenAI API URL:', openaiApiUrlContainer);
@@ -288,7 +290,7 @@ class OptionsUIModule extends BaseModule {
}, null, openaiApiUrlContainer); }, null, openaiApiUrlContainer);
// Initially hide API settings // Initially hide API settings
const apiSettings = document.querySelectorAll('.elevenlabs-setting, .openai-setting'); const apiSettings = document.querySelectorAll('.elevenlabs-tts-setting, .openai-tts-setting');
apiSettings.forEach(setting => { apiSettings.forEach(setting => {
setting.style.display = 'none'; setting.style.display = 'none';
}); });
@@ -364,7 +366,6 @@ class OptionsUIModule extends BaseModule {
// Save settings // Save settings
this.updatePreference('app', 'locale', locale); this.updatePreference('app', 'locale', locale);
this.updatePreference('tts', 'language', locale);
// Update Localization module // Update Localization module
const localization = this.getModule('localization'); const localization = this.getModule('localization');
@@ -429,22 +430,6 @@ class OptionsUIModule extends BaseModule {
}); });
} }
// Ambience Volume
if (this.elements.ambienceVolume) {
this.elements.ambienceVolume.addEventListener('input', (event) => {
const volume = parseInt(event.target.value) / 100;
console.log('Options UI: Ambience volume changed to', volume);
// Save setting
this.updatePreference('audio', 'ambienceVolume', volume);
// Update Audio Manager
const audioManager = this.getModule('audio-manager');
if (audioManager) {
audioManager.setAmbienceVolume(volume);
}
});
}
} }
/** /**
@@ -476,15 +461,15 @@ class OptionsUIModule extends BaseModule {
* @param {string} selectedSystem - Selected TTS system * @param {string} selectedSystem - Selected TTS system
*/ */
updateApiSettingsVisibility(selectedSystem) { updateApiSettingsVisibility(selectedSystem) {
const elevenLabsSettings = document.querySelectorAll('.elevenlabs-setting'); const elevenLabsSettings = document.querySelectorAll('.elevenlabs-tts-setting');
const openaiSettings = document.querySelectorAll('.openai-setting'); const openaiSettings = document.querySelectorAll('.openai-tts-setting');
elevenLabsSettings.forEach(setting => { elevenLabsSettings.forEach(setting => {
setting.style.display = selectedSystem === 'elevenlabs' ? 'flex' : 'none'; setting.style.display = selectedSystem === 'elevenlabs-tts' ? 'flex' : 'none';
}); });
openaiSettings.forEach(setting => { openaiSettings.forEach(setting => {
setting.style.display = selectedSystem === 'openai' ? 'flex' : 'none'; setting.style.display = selectedSystem === 'openai-tts' ? 'flex' : 'none';
}); });
} }
@@ -535,7 +520,7 @@ class OptionsUIModule extends BaseModule {
// Format for display // Format for display
const systems = handlers.map(handler => ({ const systems = handlers.map(handler => ({
id: handler.id, id: handler.id,
name: this.getTtsSystemName(handler.id) name: handler.displayName || handler.id
})); }));
// Populate dropdown // Populate dropdown
@@ -551,23 +536,6 @@ class OptionsUIModule extends BaseModule {
this.updateApiSettingsVisibility(this.elements.ttsSystem.value); this.updateApiSettingsVisibility(this.elements.ttsSystem.value);
} }
/**
* Get a friendly name for a TTS system
* @param {string} id - TTS system ID
* @returns {string} - Friendly name
*/
getTtsSystemName(id) {
const names = {
'none': 'None',
'browser': 'Browser',
'kokoro': 'Kokoro',
'elevenlabs': 'ElevenLabs',
'openai': 'OpenAI'
};
return names[id] || id;
}
/** /**
* Populate the voices dropdown * Populate the voices dropdown
*/ */
@@ -645,6 +613,27 @@ class OptionsUIModule extends BaseModule {
this.elements.ttsSpeed.value = Math.round(speed * 100); 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 // Audio Settings
// Master Volume // Master Volume
if (this.elements.masterVolume) { if (this.elements.masterVolume) {
@@ -754,10 +743,10 @@ class OptionsUIModule extends BaseModule {
this.elements.elevenLabsApiKey, this.elements.elevenLabsApiKey,
persistenceManager, persistenceManager,
'tts', 'tts',
'elevenlabs_api_key', 'elevenlabs-tts_api_key',
null, null,
(value) => { (value) => {
this.dispatchApiChangeEvent('api:key:change', 'elevenlabs', 'key', value); this.dispatchApiChangeEvent('api:keyChanged', 'elevenlabs-tts', 'key', value);
return value; return value;
} }
); );
@@ -767,10 +756,10 @@ class OptionsUIModule extends BaseModule {
this.elements.elevenLabsApiUrl, this.elements.elevenLabsApiUrl,
persistenceManager, persistenceManager,
'tts', 'tts',
'elevenlabs_api_url', 'elevenlabs-tts_api_url',
null, null,
(value) => { (value) => {
this.dispatchApiChangeEvent('api:url:change', 'elevenlabs', 'url', value); this.dispatchApiChangeEvent('api:urlChanged', 'elevenlabs-tts', 'url', value);
return value; return value;
} }
); );
@@ -780,10 +769,10 @@ class OptionsUIModule extends BaseModule {
this.elements.openaiApiKey, this.elements.openaiApiKey,
persistenceManager, persistenceManager,
'tts', 'tts',
'openai_api_key', 'openai-tts_api_key',
null, null,
(value) => { (value) => {
this.dispatchApiChangeEvent('api:key:change', 'openai', 'key', value); this.dispatchApiChangeEvent('api:keyChanged', 'openai-tts', 'key', value);
return value; return value;
} }
); );
@@ -793,10 +782,10 @@ class OptionsUIModule extends BaseModule {
this.elements.openaiApiUrl, this.elements.openaiApiUrl,
persistenceManager, persistenceManager,
'tts', 'tts',
'openai_api_url', 'openai-tts_api_url',
null, null,
(value) => { (value) => {
this.dispatchApiChangeEvent('api:url:change', 'openai', 'url', value); this.dispatchApiChangeEvent('api:urlChanged', 'openai-tts', 'url', value);
return value; return value;
} }
); );
@@ -873,35 +862,35 @@ class OptionsUIModule extends BaseModule {
setupApiUrlFields() { setupApiUrlFields() {
// Set up ElevenLabs API URL // Set up ElevenLabs API URL
if (this.elements.elevenLabsApiUrl) { if (this.elements.elevenLabsApiUrl) {
const savedUrl = this.getPreference('tts', 'elevenlabs_api_url'); const savedUrl = this.getPreference('tts', 'elevenlabs-tts_api_url');
const defaultUrl = 'https://api.elevenlabs.io/v1'; const defaultUrl = 'https://api.elevenlabs.io/v1';
// If no saved URL, set the default // If no saved URL, set the default
if (!savedUrl) { if (!savedUrl) {
console.log('Options UI: Setting default ElevenLabs API URL:', defaultUrl); console.log('Options UI: Setting default ElevenLabs API URL:', defaultUrl);
this.updatePreference('tts', 'elevenlabs_api_url', defaultUrl); this.updatePreference('tts', 'elevenlabs-tts_api_url', defaultUrl);
} }
} }
// Set up OpenAI API URL // Set up OpenAI API URL
if (this.elements.openaiApiUrl) { if (this.elements.openaiApiUrl) {
const savedUrl = this.getPreference('tts', 'openai_api_url'); const savedUrl = this.getPreference('tts', 'openai-tts_api_url');
const defaultUrl = 'https://api.openai.com/v1'; const defaultUrl = 'https://api.openai.com/v1';
// If no saved URL, set the default // If no saved URL, set the default
if (!savedUrl) { if (!savedUrl) {
console.log('Options UI: Setting default OpenAI API URL:', defaultUrl); console.log('Options UI: Setting default OpenAI API URL:', defaultUrl);
this.updatePreference('tts', 'openai_api_url', defaultUrl); this.updatePreference('tts', 'openai-tts_api_url', defaultUrl);
} }
} }
// Make sure API keys are initialized if not already set // Make sure API keys are initialized if not already set
if (!this.getPreference('tts', 'elevenlabs_api_key')) { if (!this.getPreference('tts', 'elevenlabs-tts_api_key')) {
this.updatePreference('tts', 'elevenlabs_api_key', ''); this.updatePreference('tts', 'elevenlabs-tts_api_key', '');
} }
if (!this.getPreference('tts', 'openai_api_key')) { if (!this.getPreference('tts', 'openai-tts_api_key')) {
this.updatePreference('tts', 'openai_api_key', ''); this.updatePreference('tts', 'openai-tts_api_key', '');
} }
} }
+4 -15
View File
@@ -29,32 +29,20 @@ class PersistenceManagerModule extends BaseModule {
// Default preferences // Default preferences
this.defaultPreferences = { this.defaultPreferences = {
animation: {
enabled: true,
speed: 50 // 0-100 scale, 50 is default
},
tts: { tts: {
enabled: false, enabled: false,
provider: 'browser', // 'browser', 'api', 'kokoro' provider: 'none',
voice: '', voice: '',
volume: 1.0,
rate: 1.0,
language: 'en-us' // Default language, will be updated during initialization
}, },
audio: { audio: {
masterVolume: 1.0, masterVolume: 1.0,
ttsVolume: 1.0,
musicVolume: 0.7, musicVolume: 0.7,
sfxVolume: 1.0, sfxVolume: 1.0,
musicEnabled: true,
sfxEnabled: true
},
accessibility: {
highContrast: false,
largerText: false
}, },
app: { app: {
locale: 'en-us', locale: 'en-us',
theme: 'default' speed: 1.0,
} }
}; };
@@ -275,6 +263,7 @@ class PersistenceManagerModule extends BaseModule {
// Save preferences // Save preferences
const success = this.savePreferences(); const success = this.savePreferences();
console.log("Saved preferences: ", category, setting, value, this.preferences)
// Dispatch event // Dispatch event
this.dispatchEvent('preference-updated', { this.dispatchEvent('preference-updated', {
+8 -6
View File
@@ -314,14 +314,15 @@ class TTSFactoryModule extends BaseModule {
for (const id in this.handlers) { for (const id in this.handlers) {
const handler = this.handlers[id]; const handler = this.handlers[id];
availableHandlers.push({ availableHandlers.push({
id: id, id: handler.getId(),
handler: handler, handler: handler,
displayName: handler.getName(),
isReady: handler.isReady === true isReady: handler.isReady === true
}); });
} }
// Add placeholder entries for important API handlers that might not be registered yet // Add placeholder entries for important API handlers that might not be registered yet
const apiHandlerIds = ['elevenlabs', 'openai']; const apiHandlerIds = ['elevenlabs-tts', 'openai-tts'];
for (const id of apiHandlerIds) { for (const id of apiHandlerIds) {
// Only add if not already in the list // Only add if not already in the list
if (!this.handlers[id] && !availableHandlers.some(h => h.id === id)) { if (!this.handlers[id] && !availableHandlers.some(h => h.id === id)) {
@@ -329,6 +330,7 @@ class TTSFactoryModule extends BaseModule {
availableHandlers.push({ availableHandlers.push({
id: id, id: id,
handler: null, handler: null,
displayName: id.split('-')[0].charAt(0).toUpperCase() + id.split('-')[0].slice(1),
isReady: false isReady: false
}); });
} }
@@ -431,10 +433,10 @@ class TTSFactoryModule extends BaseModule {
// Register handlers (in order of preference) // Register handlers (in order of preference)
const handlers = [ const handlers = [
{ id: 'kokoro', displayName: 'Kokoro TTS' }, { id: 'kokoro-tts', displayName: 'Kokoro TTS' },
{ id: 'browser', displayName: 'Browser TTS' }, { id: 'browser-tts', displayName: 'Browser TTS' },
{ id: 'elevenlabs', displayName: 'ElevenLabs TTS' }, { id: 'elevenlabs-tts', displayName: 'ElevenLabs TTS' },
{ id: 'openai', displayName: 'OpenAI TTS' } { id: 'openai-tts', displayName: 'OpenAI TTS' }
]; ];
// Register each handler // Register each handler