Initial commit

This commit is contained in:
2025-04-21 14:12:36 +02:00
commit 3fe2969b39
57 changed files with 17976 additions and 0 deletions

View File

@@ -0,0 +1,200 @@
import { EventEmitter } from '$lib/utils/EventEmitter';
export interface SpeechOptions {
pitch?: number;
rate?: number;
volume?: number;
voice?: SpeechSynthesisVoice;
}
export class AccessibilityManager extends EventEmitter {
private isSpeechEnabled: boolean = false;
private speechSynthesis: SpeechSynthesis | null = null;
private speechOptions: SpeechOptions = {
pitch: 1,
rate: 1,
volume: 0.8
};
constructor() {
super();
console.log('AccessibilityManager constructed');
// Simple initialization - no speech synthesis by default
if (typeof window !== 'undefined' && window.speechSynthesis) {
this.speechSynthesis = window.speechSynthesis;
console.log('Speech synthesis is available');
}
}
/**
* Enable or disable speech
*/
public setSpeechEnabled(enabled: boolean): void {
console.log('Setting speech enabled:', enabled);
this.isSpeechEnabled = enabled;
// Cancel speech if disabling
if (!enabled && this.speechSynthesis) {
try {
this.speechSynthesis.cancel();
} catch (error) {
console.error('Error cancelling speech:', error);
}
}
this.emit('speechEnabledChanged', enabled);
// Test speech synthesis if enabling was successful
if (this.isSpeechEnabled) {
try {
console.log('Text-to-speech is now enabled');
} catch (error) {
console.error('Error testing speech:', error);
}
}
}
/**
* Update speech options
*/
public updateSpeechOptions(options: SpeechOptions): void {
console.log('Speech options updated:', options);
// Update stored options
if (options.rate !== undefined) this.speechOptions.rate = options.rate;
if (options.pitch !== undefined) this.speechOptions.pitch = options.pitch;
if (options.volume !== undefined) this.speechOptions.volume = options.volume;
if (options.voice !== undefined) this.speechOptions.voice = options.voice;
console.log('New speech options:', this.speechOptions);
this.emit('speechOptionsChanged', this.speechOptions);
}
/**
* Speak text using text-to-speech
*/
public speak(text: string): void {
// Skip if speech is disabled
if (!this.isSpeechEnabled || !this.speechSynthesis) {
// console.log('Speech is disabled, not speaking');
return;
}
try {
// Clean and truncate text to prevent issues with large blocks
const cleanText = this.cleanTextForSpeech(text);
// Only speak if there's meaningful text after cleaning
if (cleanText && cleanText.trim().length > 0) {
console.log('Speaking text with options:', {
rate: this.speechOptions.rate,
pitch: this.speechOptions.pitch,
volume: this.speechOptions.volume
});
const utterance = new SpeechSynthesisUtterance(cleanText);
// Explicitly set options
utterance.rate = Number(this.speechOptions.rate) || 1;
utterance.pitch = Number(this.speechOptions.pitch) || 1;
utterance.volume = Number(this.speechOptions.volume) || 0.8;
console.log('Created utterance with:', {
rate: utterance.rate,
pitch: utterance.pitch,
volume: utterance.volume
});
// Add event handlers for debugging
utterance.onstart = () => console.log('Speech started');
utterance.onend = () => console.log('Speech ended');
utterance.onerror = (e) => console.error('Speech error:', e);
// Apply voice if set
if (this.speechOptions.voice) {
utterance.voice = this.speechOptions.voice;
}
// Speak the text
this.speechSynthesis.speak(utterance);
}
} catch (error) {
console.error('Error in speak:', error);
}
}
/**
* Clean text for speech synthesis
*/
private cleanTextForSpeech(text: string): string {
if (!text) return '';
try {
// Limit text length to prevent freezes with very long text
let cleanText = text.length > 200 ? text.substring(0, 200) : text;
// Remove ANSI color codes
cleanText = cleanText.replace(/\u001b\[\d+(;\d+)*m/g, '');
// Remove HTML tags
cleanText = cleanText.replace(/<[^>]*>/g, '');
return cleanText.trim();
} catch (error) {
console.error('Error cleaning text for speech:', error);
return '';
}
}
/**
* Stop current speech
*/
public stopSpeech(): void {
if (!this.speechSynthesis) return;
try {
// Make sure we cancel any pending or active speech
this.speechSynthesis.cancel();
console.log('Speech stopped');
this.emit('speechStopped');
// Force a small delay to ensure the speech engine has time to process the cancel
setTimeout(() => {
if (this.speechSynthesis && this.speechSynthesis.speaking) {
console.log('Force stopping speech again after delay');
this.speechSynthesis.cancel();
}
}, 50);
} catch (error) {
console.error('Error stopping speech:', error);
}
}
/**
* Check if speech is currently speaking
*/
public isSpeaking(): boolean {
// Ensure the speech synthesis is available
if (!this.speechSynthesis) return false;
// Check if speech is currently active
try {
return this.speechSynthesis.speaking || this.speechSynthesis.pending;
} catch (error) {
console.error('Error checking if speech is speaking:', error);
return false;
}
}
/**
* Check if speech is currently enabled
*/
public isSpeechActive(): boolean {
return this.isSpeechEnabled;
}
}