diff --git a/src/lib/accessibility/AccessibilityManager.ts b/src/lib/accessibility/AccessibilityManager.ts index a269d70..5260832 100644 --- a/src/lib/accessibility/AccessibilityManager.ts +++ b/src/lib/accessibility/AccessibilityManager.ts @@ -76,15 +76,21 @@ export class AccessibilityManager extends EventEmitter { * Speak text using text-to-speech */ public speak(text: string): void { + console.log('AccessibilityManager.speak() called with text:', text.substring(0, 50) + (text.length > 50 ? '...' : '')); + // Skip if speech is disabled if (!this.isSpeechEnabled || !this.speechSynthesis) { - // console.log('Speech is disabled, not speaking'); + console.log('Speech is disabled or speechSynthesis is null:', { + isSpeechEnabled: this.isSpeechEnabled, + speechSynthesis: !!this.speechSynthesis + }); return; } try { // Clean and truncate text to prevent issues with large blocks const cleanText = this.cleanTextForSpeech(text); + console.log('Cleaned text for speech:', cleanText.substring(0, 50) + (cleanText.length > 50 ? '...' : '')); // Only speak if there's meaningful text after cleaning if (cleanText && cleanText.trim().length > 0) { @@ -117,10 +123,12 @@ export class AccessibilityManager extends EventEmitter { utterance.voice = this.speechOptions.voice; } - - // Speak the text + console.log('Calling speechSynthesis.speak()'); this.speechSynthesis.speak(utterance); + console.log('speechSynthesis.speak() called successfully'); + } else { + console.log('Not speaking - cleaned text is empty'); } } catch (error) { console.error('Error in speak:', error); diff --git a/src/lib/components/MudConnection.svelte b/src/lib/components/MudConnection.svelte index 2a785fd..c343964 100644 --- a/src/lib/components/MudConnection.svelte +++ b/src/lib/components/MudConnection.svelte @@ -59,6 +59,27 @@ // Set up keyboard listener for speech control document.addEventListener('keydown', handleKeyDown, true); + // Subscribe to accessibility settings changes to update speech settings + const unsubAccessibility = accessibilitySettings.subscribe(settings => { + console.log(`AccessibilitySettings changed for ${profileId}`, settings); + + if (accessibilityManager) { + // Update speech enabled state + accessibilityManager.setSpeechEnabled(settings.textToSpeech); + + // Update speech options + accessibilityManager.updateSpeechOptions({ + rate: settings.speechRate, + pitch: settings.speechPitch, + volume: settings.speechVolume + }); + + console.log(`Updated AccessibilityManager settings for ${profileId}`); + } + }); + + unsubscribeFunctions.push(unsubAccessibility); + // Auto-connect if enabled if (autoConnect) { console.log('Auto-connecting profile:', profileId); @@ -98,7 +119,20 @@ // Initialize accessibility manager accessibilityManager = new AccessibilityManager(); - console.log('Accessibility manager created'); + console.log('Accessibility manager created for profile:', profileId); + + // Ensure TTS is enabled if the setting is on + if ($accessibilitySettings.textToSpeech) { + console.log(`Setting TTS enabled for profile ${profileId}`); + accessibilityManager.setSpeechEnabled(true); + + // Update speech options from settings + accessibilityManager.updateSpeechOptions({ + rate: $accessibilitySettings.speechRate, + pitch: $accessibilitySettings.speechPitch, + volume: $accessibilitySettings.speechVolume + }); + } // Set up connection from manager if it exists const existingConnection = connectionManager.getConnection(profileId); @@ -312,16 +346,12 @@ console.log(`Profile ${profileId} received data`); try { - // First check if this is the active profile - only handle messages for the active profile - if (get(activeProfileId) !== profileId) { - console.log(`Ignoring received data for inactive profile ${profileId}`); - return; - } + const isActiveProfile = get(activeProfileId) === profileId; - // Add to output history + // Always add to output history for this profile addToOutputHistory(text); - // Process triggers if available + // Process triggers if available for this profile if (triggerSystem) { try { triggerSystem.processTriggers(text); @@ -330,16 +360,28 @@ } } - // Try to use text-to-speech if enabled + // Handle text-to-speech + console.log(`TTS check for ${profileId}: isTTS=${$accessibilitySettings.textToSpeech}, isActive=${isActiveProfile}, speakAll=${$accessibilitySettings.speakAllProfiles}`); if (accessibilityManager && $accessibilitySettings.textToSpeech) { - // Use a small timeout to avoid UI blocking - setTimeout(() => { - try { - accessibilityManager.speak(text); - } catch (error) { - console.error('Error using text-to-speech:', error); - } - }, 10); + // Speak if this is active profile OR speakAllProfiles is enabled + if (isActiveProfile || $accessibilitySettings.speakAllProfiles) { + console.log(`TTS condition satisfied, will attempt to speak for ${profileId}`); + // Use a small timeout to avoid UI blocking + setTimeout(() => { + try { + // If not active profile, add profile name prefix for context + const speechText = isActiveProfile ? text : `From ${getProfileName(profileId)}: ${text}`; + console.log(`Speaking text for ${profileId}:`, speechText.substring(0, 50) + (speechText.length > 50 ? '...' : '')); + accessibilityManager.speak(speechText); + } catch (error) { + console.error('Error using text-to-speech:', error); + } + }, 10); + } else { + console.log(`TTS skipped: profile ${profileId} is not active and speakAll is disabled`); + } + } else { + console.log(`TTS not enabled for profile ${profileId} or accessibilityManager is null`); } dispatch('received', { text }); @@ -348,6 +390,15 @@ } } + /** + * Helper to get profile name for speech announcements + */ + function getProfileName(id) { + const allProfiles = get(profiles); + const profile = allProfiles.find(p => p.id === id); + return profile ? profile.name : id; + } + /** * Handle sent data */ diff --git a/src/lib/components/SettingsPanel.svelte b/src/lib/components/SettingsPanel.svelte index b4506ea..6e319b0 100644 --- a/src/lib/components/SettingsPanel.svelte +++ b/src/lib/components/SettingsPanel.svelte @@ -208,6 +208,17 @@ +
+ Speak All Profiles + +
+ Announce text from all tabs, even when they're not active +
+
+
Speech Rate
diff --git a/src/lib/settings/SettingsManager.ts b/src/lib/settings/SettingsManager.ts index 9acfc71..c7c68a5 100644 --- a/src/lib/settings/SettingsManager.ts +++ b/src/lib/settings/SettingsManager.ts @@ -11,6 +11,7 @@ export interface Settings { speechPitch: number; speechVolume: number; interruptSpeechOnEnter: boolean; + speakAllProfiles: boolean; }; ui: { isDarkMode: boolean; @@ -48,7 +49,8 @@ export class SettingsManager extends EventEmitter { speechRate: 1, speechPitch: 1, speechVolume: 1, - interruptSpeechOnEnter: true + interruptSpeechOnEnter: true, + speakAllProfiles: true }, ui: { isDarkMode: true, @@ -186,7 +188,8 @@ export class SettingsManager extends EventEmitter { speechRate: 1, speechPitch: 1, speechVolume: 1, - interruptSpeechOnEnter: true + interruptSpeechOnEnter: true, + speakAllProfiles: true }, ui: { isDarkMode: true,