@supernal/tts-widget 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/widget.ts", "../src/loader.ts"],
4
+ "sourcesContent": ["/**\n * Supernal TTS Widget\n * Embeddable text-to-speech widget for blogs and websites\n */\n\nexport interface SupernalTTSOptions {\n apiUrl?: string;\n voice?: string;\n provider?: string;\n speed?: number; // Default speed for all widgets (1.0 = normal)\n autoHash?: boolean;\n cacheExpiry?: number;\n apiKey?: string;\n showBranding?: boolean;\n version?: string;\n devMode?: boolean;\n clientSideSpeed?: boolean; // Use HTML5 playbackRate for speed adjustment (default: true)\n generationMode?: 'standard' | 'progressive' | 'realtime'; // Generation mode (default: 'standard')\n progressiveThreshold?: number; // Character count threshold to use progressive mode (default: 2000)\n}\n\nexport interface TTSGenerateOptions {\n provider: string;\n voice: string;\n speed: number;\n format?: string;\n quality?: string;\n cache?: boolean;\n}\n\nexport interface TTSResponse {\n audioUrl: string;\n hash: string;\n cached: boolean;\n duration?: number;\n cost?: number;\n}\n\nexport interface CachedAudio {\n audioUrl: string;\n metadata: Record<string, any>;\n expiry: number;\n timestamp: number;\n}\n\nexport interface WidgetData {\n text: string;\n voice: string;\n provider: string;\n speed: number;\n hash: string;\n playButton: HTMLButtonElement;\n stopButton?: HTMLButtonElement;\n voiceSelect?: HTMLSelectElement;\n speedSlider?: HTMLInputElement;\n progressSlider?: HTMLInputElement;\n advanced?: boolean;\n}\n\nexport interface WidgetConfig {\n text: string;\n voices: string[]; // Array of available voices (empty = no voice control)\n enableSpeed: boolean; // Show speed control\n enableProgress: boolean; // Show progress bar\n speed: number;\n provider: string;\n voice: string;\n apiKey?: string; // Optional per-widget API key\n}\n\nexport class SupernalTTS {\n private apiUrl: string;\n private defaultVoice: string;\n private defaultProvider: string;\n private defaultSpeed: number;\n private autoHash: boolean;\n private cacheExpiry: number;\n private apiKey?: string;\n private showBranding: boolean;\n private version: string;\n private devMode: boolean;\n private clientSideSpeed: boolean;\n private generationMode: 'standard' | 'progressive' | 'realtime';\n private progressiveThreshold: number;\n \n private cache: Map<string, CachedAudio>;\n private currentAudio: HTMLAudioElement | null = null;\n private currentButton: HTMLButtonElement | null = null;\n private currentWidget?: WidgetData;\n private isProcessing: boolean = false;\n private lastClickTime: number = 0;\n private debounceDelay: number = 300;\n private progressUpdateInterval: number | null = null; // Track interval globally\n private audioQueue: Uint8Array[] = []; // Queue for progressive streaming chunks\n\n constructor(options: SupernalTTSOptions = {}) {\n this.apiUrl = options.apiUrl || 'https://www.tts.supernal.ai';\n this.defaultVoice = options.voice || 'default';\n this.defaultProvider = options.provider || 'openai';\n this.defaultSpeed = options.speed || 1.0;\n this.autoHash = options.autoHash !== false;\n this.cacheExpiry = options.cacheExpiry || 7 * 24 * 60 * 60 * 1000; // 7 days\n this.apiKey = options.apiKey;\n console.log('[TTS Widget] Constructor - API Key provided:', !!options.apiKey);\n console.log('[TTS Widget] Constructor - API Key saved:', !!this.apiKey);\n this.showBranding = options.showBranding !== false;\n this.version = options.version || '1.0.0';\n // devMode must be explicitly enabled now (not auto-enabled by localhost)\n this.devMode = options.devMode === true;\n this.clientSideSpeed = options.clientSideSpeed !== false; // Default to true for cost savings\n this.generationMode = options.generationMode || 'standard';\n this.progressiveThreshold = options.progressiveThreshold || 2000;\n\n this.cache = new Map();\n this.loadCacheFromStorage();\n\n this.init();\n }\n\n private init(): void {\n // Auto-initialize widgets on page load\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => this.initializeWidgets());\n } else {\n this.initializeWidgets();\n }\n \n // Set up MutationObserver to detect dynamically added widgets\n // This handles client-side routing (SPA navigation) and dynamic content\n this.observeDOM();\n }\n\n private observeDOM(): void {\n // Watch for new elements being added to the DOM\n const observer = new MutationObserver((mutations) => {\n mutations.forEach((mutation) => {\n mutation.addedNodes.forEach((node) => {\n // Check if the added node is an element\n if (node.nodeType === Node.ELEMENT_NODE) {\n const element = node as HTMLElement;\n \n // Check if it's a widget or contains widgets\n if (element.classList?.contains('supernal-tts-widget')) {\n this.setupWidget(element);\n } else {\n // Check for widgets within the added element\n const widgets = element.querySelectorAll?.('.supernal-tts-widget');\n widgets?.forEach((widget) => this.setupWidget(widget as HTMLElement));\n }\n }\n });\n });\n });\n\n // Start observing the document body for changes\n observer.observe(document.body, {\n childList: true,\n subtree: true\n });\n }\n\n private initializeWidgets(): void {\n const widgets = document.querySelectorAll<HTMLElement>('.supernal-tts-widget');\n widgets.forEach(widget => this.setupWidget(widget));\n\n // Add dev mode cache clear button only if widgets exist and devMode is enabled\n if (this.devMode && widgets.length > 0) {\n this.addDevModeClearButton();\n }\n }\n\n private setupWidget(element: HTMLElement): void {\n // Check if already initialized to prevent duplicates\n if (element.classList.contains('supernal-tts-widget-initialized')) {\n return;\n }\n\n // Parse widget configuration from data attributes\n const config = this.parseWidgetConfig(element);\n \n if (!config.text) {\n console.warn('TTS Widget: No text found for widget', element);\n return;\n }\n\n // Generate hash for caching\n const hash = this.generateHash(config.text, { \n voice: config.voice, \n provider: config.provider, \n speed: config.speed \n });\n\n // Check for legacy controls attribute for backwards compatibility\n const controls = element.dataset.controls;\n if (controls === 'advanced' || controls === 'true') {\n this.setupAdvancedWidget(element, config.text, {\n voice: config.voice,\n provider: config.provider,\n speed: config.speed,\n hash,\n apiKey: config.apiKey\n });\n return;\n } else if (controls === 'compact') {\n this.setupCompactWidget(element, config.text, {\n voice: config.voice,\n provider: config.provider,\n speed: config.speed,\n hash,\n apiKey: config.apiKey\n });\n return;\n }\n\n // Use new modular widget setup\n this.setupModularWidget(element, config, hash);\n }\n\n /**\n * Parse widget configuration from data attributes\n */\n private parseWidgetConfig(element: HTMLElement): WidgetConfig {\n const text = element.dataset.text || element.textContent?.trim() || '';\n const voice = element.dataset.voice || this.defaultVoice;\n const provider = element.dataset.provider || this.defaultProvider;\n const speed = element.dataset.speed ? parseFloat(element.dataset.speed) : this.defaultSpeed;\n \n // Parse voices list (comma-separated)\n const voicesAttr = element.dataset.voices || '';\n const voices = voicesAttr ? voicesAttr.split(',').map(v => v.trim()).filter(Boolean) : [];\n \n // Parse boolean flags\n const enableSpeed = element.dataset.enableSpeed === 'true';\n const enableProgress = element.dataset.enableProgress === 'true';\n \n // Check for widget-specific API key (overrides global)\n const apiKey = element.dataset.apiKey || this.apiKey;\n\n return {\n text,\n voices,\n enableSpeed,\n enableProgress,\n speed,\n provider,\n voice,\n apiKey, // Include API key in config\n };\n }\n\n private setupBasicWidget(element: HTMLElement, text: string, options: any): void {\n const { voice, provider, speed, hash, apiKey } = options;\n \n // Check if already initialized to prevent duplicates\n if (element.classList.contains('supernal-tts-widget-initialized')) {\n return;\n }\n \n // Create container for button and branding\n const buttonContainer = document.createElement('div');\n buttonContainer.className = 'supernal-tts-button-container';\n \n // Create or find play button\n let playButton = element.querySelector<HTMLButtonElement>('.supernal-tts-play');\n if (!playButton) {\n playButton = this.createPlayButton();\n }\n \n buttonContainer.appendChild(playButton);\n\n // Add branding badge if enabled (only once)\n if (this.showBranding && !element.querySelector('.supernal-badge')) {\n const badge = this.createBrandingBadge();\n buttonContainer.appendChild(badge);\n }\n \n element.appendChild(buttonContainer);\n\n // Add widget styling\n element.classList.add('supernal-tts-widget-initialized');\n \n // Setup event listeners\n playButton.addEventListener('click', (e) => {\n e.preventDefault();\n this.handlePlayClick(playButton, text, { voice, provider, speed, hash, apiKey });\n });\n\n // Store reference for later use\n (element as any)._ttsWidget = {\n text,\n voice,\n provider,\n speed,\n hash,\n playButton\n };\n }\n\n private setupCompactWidget(element: HTMLElement, text: string, options: any): void {\n const { hash, apiKey } = options;\n let { voice, provider, speed } = options;\n \n // Check if already initialized\n if (element.classList.contains('supernal-tts-widget-initialized')) {\n return;\n }\n \n // Create container for compact controls\n const container = document.createElement('div');\n container.className = 'supernal-tts-compact-widget';\n \n // Top row: Play button + Voice selector\n const topRow = document.createElement('div');\n topRow.className = 'supernal-tts-top-row';\n \n const playButton = this.createPlayButton();\n \n // Voice selector (compact)\n const voiceSelect = document.createElement('select');\n voiceSelect.className = 'supernal-tts-voice-select';\n voiceSelect.innerHTML = `\n <option value=\"alloy\" ${voice === 'alloy' ? 'selected' : ''}>Alloy</option>\n <option value=\"echo\" ${voice === 'echo' ? 'selected' : ''}>Echo</option>\n <option value=\"fable\" ${voice === 'fable' ? 'selected' : ''}>Fable</option>\n <option value=\"onyx\" ${voice === 'onyx' ? 'selected' : ''}>Onyx</option>\n <option value=\"nova\" ${voice === 'nova' ? 'selected' : ''}>Nova</option>\n <option value=\"shimmer\" ${voice === 'shimmer' ? 'selected' : ''}>Shimmer</option>\n <option value=\"coral\" ${voice === 'coral' ? 'selected' : ''}>Coral</option>\n `;\n \n topRow.appendChild(playButton);\n topRow.appendChild(voiceSelect);\n \n // Add branding badge if enabled\n if (this.showBranding) {\n const badge = this.createBrandingBadge();\n topRow.appendChild(badge);\n }\n \n // Bottom row: Speed control\n const speedControl = document.createElement('div');\n speedControl.className = 'supernal-tts-speed-control';\n speedControl.innerHTML = `\n <label>Speed: <span class=\"supernal-tts-speed-value\">${speed}x</span></label>\n <input type=\"range\" class=\"supernal-tts-speed-slider\" min=\"0.25\" max=\"4.0\" step=\"0.25\" value=\"${speed}\">\n `;\n \n // Assemble widget\n container.appendChild(topRow);\n container.appendChild(speedControl);\n \n element.innerHTML = '';\n element.appendChild(container);\n element.classList.add('supernal-tts-widget-initialized', 'supernal-tts-widget-compact');\n \n // Setup event listeners\n const speedSlider = speedControl.querySelector<HTMLInputElement>('.supernal-tts-speed-slider')!;\n const speedValue = speedControl.querySelector<HTMLSpanElement>('.supernal-tts-speed-value')!;\n\n playButton.addEventListener('click', (e) => {\n e.preventDefault();\n speed = parseFloat(speedSlider.value);\n voice = voiceSelect.value;\n const newHash = this.generateHash(text, { voice, provider, speed });\n this.handlePlayClick(playButton, text, { voice, provider, speed, hash: newHash, apiKey });\n });\n \n speedSlider.addEventListener('input', (e) => {\n speed = parseFloat((e.target as HTMLInputElement).value);\n speedValue.textContent = `${speed}x`;\n });\n \n voiceSelect.addEventListener('change', () => {\n voice = voiceSelect.value;\n });\n \n // Store reference for later use\n (element as any)._ttsWidget = {\n text,\n voice,\n provider,\n speed,\n hash,\n playButton,\n compact: true\n };\n }\n\n private setupAdvancedWidget(element: HTMLElement, text: string, options: any): void {\n const { hash, apiKey } = options;\n let { voice, provider, speed } = options;\n \n // Create controls container\n const container = document.createElement('div');\n container.className = 'supernal-tts-advanced-widget';\n \n // Create play/pause/stop buttons\n const buttonGroup = document.createElement('div');\n buttonGroup.className = 'supernal-tts-button-group';\n \n const playButton = this.createPlayButton();\n const stopButton = this.createStopButton();\n \n buttonGroup.appendChild(playButton);\n buttonGroup.appendChild(stopButton);\n \n // Create voice selector\n const voiceSelect = document.createElement('select');\n voiceSelect.className = 'supernal-tts-voice-select';\n voiceSelect.innerHTML = `\n <option value=\"alloy\" ${voice === 'alloy' ? 'selected' : ''}>Alloy</option>\n <option value=\"echo\" ${voice === 'echo' ? 'selected' : ''}>Echo</option>\n <option value=\"fable\" ${voice === 'fable' ? 'selected' : ''}>Fable</option>\n <option value=\"onyx\" ${voice === 'onyx' ? 'selected' : ''}>Onyx</option>\n <option value=\"nova\" ${voice === 'nova' ? 'selected' : ''}>Nova</option>\n <option value=\"shimmer\" ${voice === 'shimmer' ? 'selected' : ''}>Shimmer</option>\n <option value=\"coral\" ${voice === 'coral' ? 'selected' : ''}>Coral</option>\n `;\n \n // Create speed control\n const speedControl = document.createElement('div');\n speedControl.className = 'supernal-tts-speed-control';\n speedControl.innerHTML = `\n <label>Speed: <span class=\"supernal-tts-speed-value\">${speed}x</span></label>\n <input type=\"range\" class=\"supernal-tts-speed-slider\" min=\"0.25\" max=\"4.0\" step=\"0.25\" value=\"${speed}\">\n `;\n \n // Create progress bar\n const progressBar = document.createElement('div');\n progressBar.className = 'supernal-tts-progress';\n progressBar.innerHTML = `\n <input type=\"range\" class=\"supernal-tts-progress-slider\" min=\"0\" max=\"100\" value=\"0\" step=\"0.1\">\n <div class=\"supernal-tts-time\"><span class=\"supernal-tts-current-time\">0:00</span> / <span class=\"supernal-tts-duration\">0:00</span></div>\n `;\n \n // Assemble widget\n container.appendChild(buttonGroup);\n container.appendChild(voiceSelect);\n container.appendChild(speedControl);\n container.appendChild(progressBar);\n \n // Add branding badge if enabled\n if (this.showBranding) {\n const badge = this.createBrandingBadge();\n container.appendChild(badge);\n }\n \n element.innerHTML = '';\n element.appendChild(container);\n element.classList.add('supernal-tts-widget-initialized', 'supernal-tts-widget-advanced');\n \n // Setup event listeners\n const speedSlider = speedControl.querySelector<HTMLInputElement>('.supernal-tts-speed-slider')!;\n const speedValue = speedControl.querySelector<HTMLSpanElement>('.supernal-tts-speed-value')!;\n const progressSlider = progressBar.querySelector<HTMLInputElement>('.supernal-tts-progress-slider')!;\n\n playButton.addEventListener('click', (e) => {\n e.preventDefault();\n speed = parseFloat(speedSlider.value);\n voice = voiceSelect.value;\n const newHash = this.generateHash(text, { voice, provider, speed });\n this.handlePlayClick(playButton, text, { voice, provider, speed, hash: newHash, apiKey });\n });\n \n stopButton.addEventListener('click', (e) => {\n e.preventDefault();\n this.stopAudio();\n });\n \n speedSlider.addEventListener('input', (e) => {\n speed = parseFloat((e.target as HTMLInputElement).value);\n speedValue.textContent = `${speed}x`;\n });\n \n voiceSelect.addEventListener('change', () => {\n voice = voiceSelect.value;\n });\n \n progressSlider.addEventListener('input', (e) => {\n if (this.currentAudio) {\n const percent = parseFloat((e.target as HTMLInputElement).value) / 100;\n this.currentAudio.currentTime = this.currentAudio.duration * percent;\n }\n });\n \n // Update progress bar during playback\n const updateProgress = () => {\n if (this.currentAudio && (element as any)._ttsWidget === this.currentWidget) {\n const percent = (this.currentAudio.currentTime / this.currentAudio.duration) * 100;\n progressSlider.value = percent.toString();\n \n const currentTime = this.formatTime(this.currentAudio.currentTime);\n const duration = this.formatTime(this.currentAudio.duration);\n progressBar.querySelector('.supernal-tts-current-time')!.textContent = currentTime;\n progressBar.querySelector('.supernal-tts-duration')!.textContent = duration;\n }\n };\n \n setInterval(updateProgress, 100);\n \n // Store reference for later use\n (element as any)._ttsWidget = {\n text,\n voice,\n provider,\n speed,\n hash,\n playButton,\n stopButton,\n voiceSelect,\n speedSlider,\n progressSlider,\n advanced: true\n };\n \n this.currentWidget = (element as any)._ttsWidget;\n }\n\n /**\n * Setup modular widget with conditional features based on config\n */\n private setupModularWidget(element: HTMLElement, config: WidgetConfig, hash: string): void {\n let currentVoice = config.voice;\n let currentSpeed = config.speed;\n const { text, voices, enableSpeed, enableProgress, provider, apiKey } = config;\n let savedPosition: number = 0; // Track playback position for voice/speed changes\n\n // Create main container\n const container = document.createElement('div');\n container.className = 'supernal-tts-modular-widget';\n\n // Create controls row\n const controlsRow = document.createElement('div');\n controlsRow.className = 'supernal-tts-controls-row';\n\n // 1. Play button (compact - icon only)\n const playButton = this.createPlayButton(true);\n controlsRow.appendChild(playButton);\n\n // 2. Voice dropdown (conditional)\n let voiceDropdown: HTMLElement | null = null;\n if (voices.length > 0) {\n voiceDropdown = this.createVoiceDropdown(voices, currentVoice);\n controlsRow.appendChild(voiceDropdown);\n \n // Listen for voice changes\n voiceDropdown.addEventListener('voice-changed', ((e: CustomEvent) => {\n const newVoice = e.detail.voice;\n \n // If voice changed while playing, save position and regenerate\n if (newVoice !== currentVoice && this.currentButton === playButton && this.currentAudio) {\n currentVoice = newVoice;\n \n // Save current playback position as percentage\n if (this.currentAudio.duration > 0) {\n savedPosition = this.currentAudio.currentTime / this.currentAudio.duration;\n }\n \n // Get the progress bar reference\n const widgetData = (element as any)._ttsWidget;\n const progressBar = widgetData?.progressBar;\n \n // Clean stop: clear audio and intervals, but keep progress bar visible\n this.cleanStopAudioKeepProgress(progressBar);\n \n // Trigger new playback with updated voice and saved position\n setTimeout(() => {\n this.handlePlayClickFromPosition(playButton, text, {\n voice: currentVoice,\n provider,\n speed: currentSpeed,\n hash: this.generateHash(text, { voice: currentVoice, provider, speed: currentSpeed }),\n progressBar,\n apiKey\n }, savedPosition);\n }, 50);\n } else {\n currentVoice = newVoice;\n }\n }) as EventListener);\n }\n\n // 3. Speed dropdown (conditional)\n let speedDropdown: HTMLElement | null = null;\n if (enableSpeed) {\n speedDropdown = this.createSpeedDropdown(currentSpeed);\n controlsRow.appendChild(speedDropdown);\n \n // Listen for speed changes\n speedDropdown.addEventListener('speed-changed', ((e: CustomEvent) => {\n const newSpeed = e.detail.speed;\n \n if (newSpeed !== currentSpeed) {\n currentSpeed = newSpeed;\n \n // Client-side speed adjustment: use HTML5 playbackRate with pitch preservation\n if (this.clientSideSpeed && this.currentAudio && this.currentButton === playButton) {\n // Instant speed change via playbackRate with pitch preservation - no regeneration needed!\n // The browser's time-stretching algorithm maintains pitch quality\n this.currentAudio.playbackRate = newSpeed;\n } \n // Server-side speed adjustment: regenerate audio (expensive but higher quality)\n else if (!this.clientSideSpeed && this.currentButton === playButton && this.currentAudio) {\n // Save current playback position as percentage\n if (this.currentAudio.duration > 0) {\n savedPosition = this.currentAudio.currentTime / this.currentAudio.duration;\n }\n \n // Get the progress bar reference\n const widgetData = (element as any)._ttsWidget;\n const progressBar = widgetData?.progressBar;\n \n // Clean stop: clear audio and intervals, but keep progress bar visible\n this.cleanStopAudioKeepProgress(progressBar);\n \n // Trigger new playback with updated speed and saved position\n setTimeout(() => {\n this.handlePlayClickFromPosition(playButton, text, {\n voice: currentVoice,\n provider,\n speed: currentSpeed,\n hash: this.generateHash(text, { voice: currentVoice, provider, speed: currentSpeed }),\n progressBar,\n apiKey\n }, savedPosition);\n }, 50);\n }\n }\n }) as EventListener);\n }\n\n // 4. Branding badge\n if (this.showBranding) {\n const badge = this.createBrandingBadge();\n controlsRow.appendChild(badge);\n }\n\n container.appendChild(controlsRow);\n\n // 5. Progress bar (conditional)\n let progressBar: HTMLElement | null = null;\n if (enableProgress) {\n progressBar = this.createProgressBar();\n container.appendChild(progressBar);\n }\n\n // IMPORTANT: Prepend controls, keep original content\n element.insertBefore(container, element.firstChild);\n element.classList.add('supernal-tts-widget-initialized', 'supernal-tts-widget-modular');\n \n // Add has-progress class if progress bar is enabled\n if (enableProgress) {\n element.classList.add('has-progress');\n }\n\n // Setup play button handler\n playButton.addEventListener('click', (e) => {\n e.preventDefault();\n const newHash = this.generateHash(text, { voice: currentVoice, provider, speed: currentSpeed });\n this.handlePlayClick(playButton, text, {\n voice: currentVoice,\n provider,\n speed: currentSpeed,\n hash: newHash,\n progressBar,\n apiKey\n });\n });\n\n // Store reference\n (element as any)._ttsWidget = {\n text,\n voice: currentVoice,\n provider,\n speed: currentSpeed,\n hash,\n playButton,\n voiceDropdown,\n speedDropdown,\n progressBar\n };\n }\n\n /**\n * Create voice dropdown with person icon button\n */\n private createVoiceDropdown(voices: string[], selectedVoice: string): HTMLElement {\n const container = document.createElement('div');\n container.className = 'supernal-tts-voice-control';\n\n // Person icon toggle button (no background)\n const toggleButton = document.createElement('button');\n toggleButton.className = 'supernal-tts-voice-toggle';\n toggleButton.setAttribute('aria-label', 'Select voice');\n toggleButton.setAttribute('aria-expanded', 'false');\n toggleButton.innerHTML = `\n <svg class=\"supernal-tts-voice-icon\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z\"/>\n </svg>\n `;\n\n // Dropdown menu (hidden by default)\n const dropdown = document.createElement('div');\n dropdown.className = 'supernal-tts-voice-dropdown hidden';\n dropdown.setAttribute('role', 'menu');\n\n voices.forEach(voice => {\n const option = document.createElement('button');\n option.className = 'supernal-tts-voice-option';\n option.textContent = this.formatVoiceName(voice);\n option.dataset.voice = voice;\n option.setAttribute('role', 'menuitem');\n \n if (voice === selectedVoice) {\n option.classList.add('active');\n }\n\n option.addEventListener('click', (e) => {\n e.stopPropagation();\n e.preventDefault();\n \n // Update active state\n dropdown.querySelectorAll('.supernal-tts-voice-option').forEach(opt => {\n opt.classList.remove('active');\n });\n option.classList.add('active');\n\n // Emit change event\n container.dispatchEvent(new CustomEvent('voice-changed', { \n detail: { voice } \n }));\n });\n\n dropdown.appendChild(option);\n });\n\n let hideTimeout: number | null = null;\n\n // Show dropdown on hover with smart positioning\n container.addEventListener('mouseenter', () => {\n if (hideTimeout) {\n clearTimeout(hideTimeout);\n hideTimeout = null;\n }\n dropdown.classList.remove('hidden');\n toggleButton.setAttribute('aria-expanded', 'true');\n \n // Smart positioning: check if dropdown would go off-screen\n setTimeout(() => {\n const rect = dropdown.getBoundingClientRect();\n const viewportHeight = window.innerHeight;\n \n if (rect.bottom > viewportHeight && rect.top > rect.height) {\n // Not enough space below, position above\n dropdown.style.bottom = '100%';\n dropdown.style.top = 'auto';\n } else {\n // Default: position below\n dropdown.style.top = 'calc(100% + 4px)';\n dropdown.style.bottom = 'auto';\n }\n }, 0);\n });\n\n container.addEventListener('mouseleave', (e) => {\n // Delay hiding to allow moving to dropdown\n hideTimeout = window.setTimeout(() => {\n dropdown.classList.add('hidden');\n toggleButton.setAttribute('aria-expanded', 'false');\n }, 150);\n });\n\n // Keep dropdown open when hovering over it\n dropdown.addEventListener('mouseenter', () => {\n if (hideTimeout) {\n clearTimeout(hideTimeout);\n hideTimeout = null;\n }\n });\n\n dropdown.addEventListener('mouseleave', () => {\n hideTimeout = window.setTimeout(() => {\n dropdown.classList.add('hidden');\n toggleButton.setAttribute('aria-expanded', 'false');\n }, 150);\n });\n\n // Also allow click to toggle\n toggleButton.addEventListener('click', (e) => {\n e.stopPropagation();\n const isHidden = dropdown.classList.contains('hidden');\n dropdown.classList.toggle('hidden');\n toggleButton.setAttribute('aria-expanded', isHidden ? 'true' : 'false');\n });\n\n container.appendChild(toggleButton);\n container.appendChild(dropdown);\n\n return container;\n }\n\n /**\n * Create speed dropdown with preset values (hover to reveal)\n */\n private createSpeedDropdown(selectedSpeed: number): HTMLElement {\n const container = document.createElement('div');\n container.className = 'supernal-tts-speed-control';\n\n // Speedometer/gauge icon button (no background)\n const button = document.createElement('button');\n button.className = 'supernal-tts-speed-toggle';\n button.setAttribute('aria-label', 'Adjust playback speed');\n button.innerHTML = `\n <svg class=\"supernal-tts-speed-icon\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M20.38 8.57l-1.23 1.85a8 8 0 0 1-.22 7.58H5.07A8 8 0 0 1 15.58 6.85l1.85-1.23A10 10 0 0 0 3.35 19a2 2 0 0 0 1.72 1h13.85a2 2 0 0 0 1.74-1 10 10 0 0 0-.27-10.44z\"/>\n <path d=\"M10.59 15.41a2 2 0 0 0 2.83 0l5.66-8.49-8.49 5.66a2 2 0 0 0 0 2.83z\"/>\n </svg>\n `;\n\n // Dropdown menu (hidden by default, shows on hover)\n const dropdown = document.createElement('div');\n dropdown.className = 'supernal-tts-speed-dropdown hidden';\n dropdown.setAttribute('role', 'menu');\n\n // Speed range: 0.6 to 3.0 in steps of 0.2\n const speeds: number[] = [];\n for (let speed = 0.6; speed <= 3.0; speed += 0.2) {\n speeds.push(Math.round(speed * 10) / 10); // Round to 1 decimal\n }\n \n speeds.forEach(speed => {\n const option = document.createElement('button');\n option.className = 'supernal-tts-speed-option';\n option.textContent = `${speed.toFixed(1)}x`;\n option.dataset.speed = speed.toString();\n option.setAttribute('role', 'menuitem');\n\n if (Math.abs(speed - selectedSpeed) < 0.01) {\n option.classList.add('active');\n }\n\n option.addEventListener('click', (e) => {\n e.stopPropagation();\n e.preventDefault();\n\n // Update active state\n dropdown.querySelectorAll('.supernal-tts-speed-option').forEach(opt => {\n opt.classList.remove('active');\n });\n option.classList.add('active');\n\n // Emit change event\n container.dispatchEvent(new CustomEvent('speed-changed', { \n detail: { speed } \n }));\n });\n\n dropdown.appendChild(option);\n });\n\n let hideTimeout: number | null = null;\n\n // Show dropdown on hover with smart positioning\n container.addEventListener('mouseenter', () => {\n if (hideTimeout) {\n clearTimeout(hideTimeout);\n hideTimeout = null;\n }\n dropdown.classList.remove('hidden');\n \n // Smart positioning: check if dropdown would go off-screen\n setTimeout(() => {\n const rect = dropdown.getBoundingClientRect();\n const viewportHeight = window.innerHeight;\n \n if (rect.bottom > viewportHeight && rect.top > rect.height) {\n // Not enough space below, position above\n dropdown.style.bottom = '100%';\n dropdown.style.top = 'auto';\n } else {\n // Default: position below\n dropdown.style.top = 'calc(100% + 4px)';\n dropdown.style.bottom = 'auto';\n }\n }, 0);\n });\n\n container.addEventListener('mouseleave', () => {\n // Delay hiding to allow moving to dropdown\n hideTimeout = window.setTimeout(() => {\n dropdown.classList.add('hidden');\n }, 150);\n });\n\n // Keep dropdown open when hovering over it\n dropdown.addEventListener('mouseenter', () => {\n if (hideTimeout) {\n clearTimeout(hideTimeout);\n hideTimeout = null;\n }\n });\n\n dropdown.addEventListener('mouseleave', () => {\n hideTimeout = window.setTimeout(() => {\n dropdown.classList.add('hidden');\n }, 150);\n });\n\n container.appendChild(button);\n container.appendChild(dropdown);\n\n return container;\n }\n\n /**\n * Create seekable progress bar with time display\n */\n private createProgressBar(): HTMLElement {\n const container = document.createElement('div');\n container.className = 'supernal-tts-progress-container';\n\n const slider = document.createElement('input');\n slider.type = 'range';\n slider.className = 'supernal-tts-progress-slider';\n slider.min = '0';\n slider.max = '100';\n slider.value = '0';\n slider.step = '0.1';\n slider.setAttribute('aria-label', 'Seek audio position');\n\n const timeDisplay = document.createElement('div');\n timeDisplay.className = 'supernal-tts-time-display';\n timeDisplay.innerHTML = '<span class=\"current\">0:00</span> / <span class=\"duration\">0:00</span>';\n\n // Seeking functionality\n slider.addEventListener('input', (e) => {\n if (this.currentAudio) {\n const percentage = parseFloat((e.target as HTMLInputElement).value);\n this.currentAudio.currentTime = (percentage / 100) * this.currentAudio.duration;\n }\n });\n\n container.appendChild(slider);\n container.appendChild(timeDisplay);\n\n return container;\n }\n\n /**\n * Format seconds to MM:SS\n */\n private formatTime(seconds: number): string {\n if (!seconds || isNaN(seconds)) return '0:00';\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n return `${mins}:${secs.toString().padStart(2, '0')}`;\n }\n\n /**\n * Format voice name for display\n */\n private formatVoiceName(voice: string): string {\n // Capitalize first letter and replace dashes/underscores with spaces\n return voice\n .replace(/[-_]/g, ' ')\n .split(' ')\n .map(word => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n }\n\n private createStopButton(): HTMLButtonElement {\n const button = document.createElement('button');\n button.className = 'supernal-tts-stop';\n button.innerHTML = `\n <svg class=\"supernal-tts-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <rect x=\"6\" y=\"6\" width=\"12\" height=\"12\"/>\n </svg>\n <span class=\"supernal-tts-text\">Stop</span>\n `;\n button.setAttribute('aria-label', 'Stop text-to-speech');\n return button;\n }\n\n private createPlayButton(compact: boolean = false): HTMLButtonElement {\n const button = document.createElement('button');\n button.className = 'supernal-tts-play';\n \n if (compact) {\n // Compact mode: icon only, circular button\n button.classList.add('supernal-tts-play-compact');\n button.innerHTML = `\n <svg class=\"supernal-tts-icon supernal-tts-play-icon\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M8 5v14l11-7z\"/>\n </svg>\n <svg class=\"supernal-tts-icon supernal-tts-loading-icon\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" opacity=\"0.25\"></circle>\n <path d=\"M12 2a10 10 0 0 1 10 10\" stroke-linecap=\"round\"></path>\n </svg>\n `;\n } else {\n // Full mode: icon + text\n button.innerHTML = `\n <svg class=\"supernal-tts-icon supernal-tts-play-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path d=\"M8 5v14l11-7z\"/>\n </svg>\n <svg class=\"supernal-tts-icon supernal-tts-loading-icon\" width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" opacity=\"0.25\"></circle>\n <path d=\"M12 2a10 10 0 0 1 10 10\" stroke-linecap=\"round\"></path>\n </svg>\n <span class=\"supernal-tts-text\">Listen</span>\n `;\n }\n \n button.setAttribute('aria-label', 'Play text-to-speech');\n return button;\n }\n\n private createBrandingBadge(): HTMLAnchorElement {\n const badge = document.createElement('a');\n badge.className = 'supernal-badge';\n badge.href = 'https://www.tts.supernal.ai';\n badge.target = '_blank';\n badge.rel = 'noopener noreferrer';\n badge.title = 'Powered by Supernal TTS';\n badge.setAttribute('aria-label', 'Powered by Supernal TTS');\n\n // Use new TTS logo (~+) from brand-kit hosted on CDN\n const img = document.createElement('img');\n img.src = 'https://unpkg.com/@supernal/tts-widget@latest/dist/supernal-tts-logo.svg';\n img.alt = 'Supernal TTS';\n img.className = 'supernal-logo-img';\n img.style.display = 'block';\n img.style.width = '24px';\n img.style.height = '24px';\n\n // Fallback to ~+ text if image fails to load\n img.onerror = () => {\n badge.innerHTML = '~+';\n badge.style.fontSize = '16px';\n badge.style.fontWeight = '700';\n badge.style.display = 'flex';\n badge.style.color = '#0066ff';\n };\n\n badge.appendChild(img);\n return badge;\n }\n\n /**\n * Dev Mode Cache Clear Button\n * Adds a minimizable floating button in dev mode to clear local cache\n * Only appears on pages that have TTS widgets\n */\n private addDevModeClearButton(): void {\n // Only add once\n if (document.getElementById('supernal-tts-dev-clear-cache')) {\n return;\n }\n\n // Create container\n const container = document.createElement('div');\n container.id = 'supernal-tts-dev-clear-cache';\n container.style.cssText = `\n position: fixed;\n bottom: 20px;\n right: 20px;\n z-index: 100;\n font-family: system-ui, -apple-system, sans-serif;\n `;\n\n // Minimized button (trash icon only)\n const minimizedBtn = document.createElement('button');\n minimizedBtn.className = 'supernal-tts-dev-button-minimized';\n minimizedBtn.innerHTML = '\uD83D\uDDD1\uFE0F';\n minimizedBtn.title = 'Clear TTS Cache (Dev Mode)';\n minimizedBtn.style.cssText = `\n display: none;\n background: #6c757d;\n color: white;\n border: none;\n padding: 8px;\n border-radius: 50%;\n width: 36px;\n height: 36px;\n font-size: 16px;\n cursor: pointer;\n box-shadow: 0 2px 8px rgba(0,0,0,0.3);\n transition: all 0.2s ease;\n `;\n\n // Full button\n const fullBtn = document.createElement('button');\n fullBtn.className = 'supernal-tts-dev-button';\n fullBtn.style.cssText = `\n background: #dc3545;\n color: white;\n border: none;\n padding: 8px 12px;\n border-radius: 5px;\n font-size: 12px;\n cursor: pointer;\n box-shadow: 0 2px 8px rgba(0,0,0,0.3);\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n gap: 6px;\n `;\n fullBtn.innerHTML = `\n <span>\uD83D\uDDD1\uFE0F</span>\n <span>Clear Cache</span>\n <span style=\"opacity: 0.7; font-size: 10px;\">\u00D7</span>\n `;\n\n // Hover effects\n const addHoverEffect = (btn: HTMLButtonElement) => {\n btn.addEventListener('mouseenter', () => {\n btn.style.transform = 'scale(1.05)';\n btn.style.boxShadow = '0 4px 12px rgba(0,0,0,0.4)';\n });\n\n btn.addEventListener('mouseleave', () => {\n btn.style.transform = 'scale(1)';\n btn.style.boxShadow = '0 2px 8px rgba(0,0,0,0.3)';\n });\n };\n\n addHoverEffect(fullBtn);\n addHoverEffect(minimizedBtn);\n\n // Clear cache handler\n const clearCache = () => {\n const count = this.cache.size;\n this.cache.clear();\n localStorage.removeItem('supernal-tts-cache');\n\n // Visual feedback\n fullBtn.innerHTML = `<span>\u2713</span><span>Cleared ${count}</span><span style=\"opacity: 0.7; font-size: 10px;\">\u00D7</span>`;\n fullBtn.style.background = '#28a745';\n\n setTimeout(() => {\n fullBtn.innerHTML = `<span>\uD83D\uDDD1\uFE0F</span><span>Clear Cache</span><span style=\"opacity: 0.7; font-size: 10px;\">\u00D7</span>`;\n fullBtn.style.background = '#dc3545';\n }, 2000);\n\n console.log(`[Supernal TTS Dev] Cleared ${count} cached items`);\n };\n\n // Minimize handler\n fullBtn.addEventListener('click', (e) => {\n e.preventDefault();\n const clickX = e.clientX - (e.target as HTMLElement).getBoundingClientRect().left;\n const btnWidth = fullBtn.getBoundingClientRect().width;\n\n // If clicked on the \u00D7 (right 20% of button), minimize\n if (clickX > btnWidth * 0.8) {\n fullBtn.style.display = 'none';\n minimizedBtn.style.display = 'block';\n localStorage.setItem('supernal-tts-dev-minimized', 'true');\n } else {\n clearCache();\n }\n });\n\n // Expand handler\n minimizedBtn.addEventListener('click', (e) => {\n e.preventDefault();\n minimizedBtn.style.display = 'none';\n fullBtn.style.display = 'flex';\n localStorage.setItem('supernal-tts-dev-minimized', 'false');\n });\n\n // Restore minimized state\n const isMinimized = localStorage.getItem('supernal-tts-dev-minimized') === 'true';\n if (isMinimized) {\n fullBtn.style.display = 'none';\n minimizedBtn.style.display = 'block';\n }\n\n container.appendChild(fullBtn);\n container.appendChild(minimizedBtn);\n\n // Add to DOM when ready\n if (document.body) {\n document.body.appendChild(container);\n } else {\n document.addEventListener('DOMContentLoaded', () => {\n document.body.appendChild(container);\n });\n }\n }\n\n private async handlePlayClick(button: HTMLButtonElement, text: string, options: any): Promise<void> {\n const { hash, progressBar } = options;\n \n // If currently playing this button, pause it\n if (button.classList.contains('supernal-tts-playing') && this.currentButton === button) {\n this.pauseAudio();\n return;\n }\n \n // If currently paused on this button, resume it\n if (button.classList.contains('supernal-tts-paused') && this.currentButton === button) {\n this.resumeAudio();\n return;\n }\n \n // Don't allow starting new audio while processing\n if (this.isProcessing) {\n return;\n }\n \n // If another button is playing, stop it first\n if (this.currentAudio && this.currentButton && this.currentButton !== button) {\n this.stopAudio();\n }\n\n try {\n this.isProcessing = true;\n \n // Update button state\n this.setButtonState(button, 'loading');\n \n // Check cache first\n let audioUrl = this.getCachedAudioUrl(hash);\n \n if (!audioUrl) {\n // Determine if we should use progressive mode\n const useProgressive = (\n this.generationMode === 'progressive' || \n (this.generationMode === 'standard' && text.length > this.progressiveThreshold)\n );\n \n if (useProgressive) {\n // Use progressive SSE streaming\n await this.generateAudioProgressive(text, options, button, progressBar);\n return; // generateAudioProgressive handles playback\n } else {\n // Standard generation\n const response = await this.generateAudio(text, options);\n // Convert relative audioUrl to absolute URL\n audioUrl = response.audioUrl.startsWith('http') \n ? response.audioUrl \n : `${this.apiUrl}${response.audioUrl}`;\n \n // Cache the result\n this.cacheAudioUrl(hash, audioUrl, response);\n }\n }\n \n // Play audio with progress bar updates\n await this.playAudio(audioUrl, button, progressBar);\n \n } catch (error) {\n console.error('TTS Error:', error);\n this.setButtonState(button, 'error');\n \n // Show user-friendly error\n this.showError(button, (error as Error).message);\n \n setTimeout(() => this.setButtonState(button, 'ready'), 3000);\n this.isProcessing = false;\n }\n }\n\n private async generateAudio(text: string, options: any): Promise<TTSResponse> {\n const body = {\n text,\n options: {\n provider: options.provider,\n voice: options.voice,\n speed: options.speed || 1.0\n }\n };\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json'\n };\n\n // Use widget-specific API key if provided, otherwise use global\n const apiKey = options.apiKey || this.apiKey;\n console.log('[TTS Widget] Generate Audio - API Key present:', !!apiKey);\n console.log('[TTS Widget] Generate Audio - this.apiKey:', !!this.apiKey);\n console.log('[TTS Widget] Generate Audio - options.apiKey:', !!options.apiKey);\n if (apiKey) {\n headers['Authorization'] = `Bearer ${apiKey}`;\n console.log('[TTS Widget] Generate Audio - Authorization header added');\n } else {\n console.warn('[TTS Widget] Generate Audio - NO API KEY, Authorization header NOT added');\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/api/v1/generate`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body)\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.error || `HTTP ${response.status}: ${response.statusText}`);\n }\n\n const data = await response.json();\n \n return {\n audioUrl: `${this.apiUrl}${data.audioUrl}`,\n hash: data.hash,\n cached: data.cached,\n duration: data.duration,\n cost: data.cost\n };\n } catch (error) {\n // Log the actual error for debugging\n console.error('TTS generation failed:', error);\n \n // Re-throw with more context but preserve the original error\n if ((error as Error).message.includes('Failed to fetch') || (error as Error).message.includes('NetworkError')) {\n const originalMessage = (error as Error).message;\n throw new Error(`TTS API unreachable: ${originalMessage}. Check if ${this.apiUrl} is accessible.`);\n }\n \n // Re-throw other errors as-is\n throw error;\n }\n }\n\n /**\n * Generate audio using progressive SSE streaming\n * Updates progress bar in real-time as chunks complete\n */\n private async generateAudioProgressive(\n text: string, \n options: any, \n button: HTMLButtonElement, \n progressBar?: HTMLElement | null\n ): Promise<void> {\n const body = {\n text,\n options: {\n provider: options.provider,\n voice: options.voice,\n speed: options.speed || 1.0\n }\n };\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'Accept': 'text/event-stream'\n };\n\n const apiKey = options.apiKey || this.apiKey;\n if (apiKey) {\n headers['Authorization'] = `Bearer ${apiKey}`;\n }\n\n try {\n const response = await fetch(`${this.apiUrl}/api/v1/generate-progressive`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body)\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(error.error || `HTTP ${response.status}: ${response.statusText}`);\n }\n\n if (!response.body) {\n throw new Error('No response body for SSE stream');\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let totalChunks = 0;\n let completedChunks = 0;\n\n // Update progress bar if available\n const updateProgress = (completed: number, total: number) => {\n if (progressBar) {\n const percentage = (completed / total) * 100;\n const slider = progressBar.querySelector<HTMLInputElement>('.supernal-tts-progress-slider');\n if (slider) {\n slider.value = percentage.toString();\n }\n \n const currentSpan = progressBar.querySelector<HTMLElement>('.current');\n if (currentSpan) {\n currentSpan.textContent = `Chunk ${completed}/${total}`;\n }\n }\n };\n\n while (true) {\n const { done, value } = await reader.read();\n \n if (done) break;\n \n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n\\n');\n buffer = lines.pop() || '';\n \n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n \n try {\n const event = JSON.parse(line.substring(6));\n \n switch (event.type) {\n case 'started':\n totalChunks = event.totalChunks;\n console.log(`[Progressive TTS] Started: ${totalChunks} chunks`);\n this.setButtonState(button, 'loading');\n break;\n \n case 'progress':\n completedChunks = event.completed;\n console.log(`[Progressive TTS] Progress: ${completedChunks}/${totalChunks}`);\n updateProgress(completedChunks, totalChunks);\n break;\n \n case 'complete':\n console.log(`[Progressive TTS] Complete:`, event);\n const audioUrl = event.audioUrl.startsWith('http') \n ? event.audioUrl \n : `${this.apiUrl}${event.audioUrl}`;\n \n // Cache the final result\n this.cacheAudioUrl(options.hash, audioUrl, {\n hash: event.hash,\n cached: false,\n duration: event.duration,\n cost: event.cost\n });\n \n // Play the final stitched audio\n await this.playAudio(audioUrl, button, progressBar);\n break;\n \n case 'error':\n throw new Error(event.message || 'Progressive generation failed');\n }\n } catch (parseError) {\n console.warn('[Progressive TTS] Failed to parse event:', line, parseError);\n }\n }\n }\n } catch (error) {\n console.error('Progressive TTS generation failed:', error);\n this.isProcessing = false;\n throw error;\n }\n }\n\n private async playAudio(audioUrl: string, button: HTMLButtonElement, progressBar?: HTMLElement | null, playbackSpeed?: number): Promise<void> {\n return new Promise((resolve, reject) => {\n const audio = new Audio(audioUrl);\n this.currentAudio = audio;\n this.currentButton = button;\n \n // Enable pitch preservation for client-side speed control\n // This uses native browser time-stretching to maintain pitch quality\n if (this.clientSideSpeed) {\n audio.preservesPitch = true;\n // Fallback for older browsers\n (audio as any).mozPreservesPitch = true;\n (audio as any).webkitPreservesPitch = true;\n }\n \n // Set playback rate if provided (for client-side speed control)\n if (playbackSpeed !== undefined && this.clientSideSpeed) {\n audio.playbackRate = playbackSpeed;\n }\n \n // Clear any existing interval before starting new one\n if (this.progressUpdateInterval) {\n clearInterval(this.progressUpdateInterval);\n this.progressUpdateInterval = null;\n }\n \n audio.addEventListener('loadstart', () => {\n if (this.currentAudio === audio) {\n this.setButtonState(button, 'loading');\n }\n });\n \n audio.addEventListener('canplay', () => {\n if (this.currentAudio === audio) {\n this.setButtonState(button, 'playing');\n this.isProcessing = false;\n const playPromise = audio.play();\n if (playPromise !== undefined) {\n playPromise.catch(error => {\n console.error('Audio play failed:', error);\n if (this.currentAudio === audio) {\n this.setButtonState(button, 'error');\n this.currentAudio = null;\n this.currentButton = null;\n this.isProcessing = false;\n reject(new Error('Audio playback failed'));\n }\n });\n }\n \n // Start progress bar updates using global interval\n if (progressBar) {\n progressBar.classList.add('playing');\n this.progressUpdateInterval = window.setInterval(() => {\n this.updateProgressBar(audio, progressBar);\n }, 100);\n }\n }\n });\n \n audio.addEventListener('ended', () => {\n if (this.currentAudio === audio) {\n this.setButtonState(button, 'ready');\n this.currentAudio = null;\n this.currentButton = null;\n \n // Clear progress bar updates\n if (this.progressUpdateInterval) {\n clearInterval(this.progressUpdateInterval);\n this.progressUpdateInterval = null;\n }\n if (progressBar) {\n progressBar.classList.remove('playing');\n // Reset progress bar\n const slider = progressBar.querySelector<HTMLInputElement>('.supernal-tts-progress-slider');\n if (slider) slider.value = '0';\n const timeDisplay = progressBar.querySelector('.supernal-tts-time-display');\n if (timeDisplay) timeDisplay.innerHTML = '<span class=\"current\">0:00</span> / <span class=\"duration\">0:00</span>';\n }\n \n resolve();\n }\n });\n \n audio.addEventListener('error', (e) => {\n if (this.currentAudio === audio) {\n this.setButtonState(button, 'error');\n this.currentAudio = null;\n this.currentButton = null;\n \n // Clear progress bar updates\n if (this.progressUpdateInterval) {\n clearInterval(this.progressUpdateInterval);\n this.progressUpdateInterval = null;\n }\n if (progressBar) {\n progressBar.classList.remove('playing');\n }\n \n reject(new Error('Audio playback failed'));\n }\n });\n \n audio.load();\n });\n }\n\n /**\n * Update progress bar during playback\n */\n private updateProgressBar(audio: HTMLAudioElement, progressBar: HTMLElement): void {\n if (!audio || !progressBar) return;\n \n const slider = progressBar.querySelector<HTMLInputElement>('.supernal-tts-progress-slider');\n const currentTimeSpan = progressBar.querySelector<HTMLElement>('.current');\n const durationSpan = progressBar.querySelector<HTMLElement>('.duration');\n \n if (slider && !isNaN(audio.duration)) {\n const percentage = (audio.currentTime / audio.duration) * 100;\n slider.value = percentage.toString();\n }\n \n if (currentTimeSpan) {\n currentTimeSpan.textContent = this.formatTime(audio.currentTime);\n }\n \n if (durationSpan && !isNaN(audio.duration)) {\n durationSpan.textContent = this.formatTime(audio.duration);\n }\n }\n\n private pauseAudio(): void {\n if (this.currentAudio && !this.currentAudio.paused) {\n this.currentAudio.pause();\n }\n \n if (this.currentButton) {\n this.setButtonState(this.currentButton, 'paused');\n }\n }\n\n private resumeAudio(): void {\n if (this.currentAudio && this.currentAudio.paused) {\n const playPromise = this.currentAudio.play();\n if (playPromise !== undefined) {\n playPromise.then(() => {\n if (this.currentButton) {\n this.setButtonState(this.currentButton, 'playing');\n }\n }).catch(error => {\n console.error('Audio resume failed:', error);\n if (this.currentButton) {\n this.setButtonState(this.currentButton, 'error');\n }\n });\n }\n }\n }\n\n private stopAudio(): void {\n // Clear interval first\n if (this.progressUpdateInterval) {\n clearInterval(this.progressUpdateInterval);\n this.progressUpdateInterval = null;\n }\n \n if (this.currentAudio) {\n this.currentAudio.pause();\n this.currentAudio.currentTime = 0;\n this.currentAudio = null;\n }\n \n if (this.currentButton) {\n this.setButtonState(this.currentButton, 'ready');\n this.currentButton = null;\n }\n \n this.isProcessing = false;\n }\n\n /**\n * Clean stop audio with proper progress bar cleanup\n * Used when regenerating audio with different parameters\n */\n private cleanStopAudio(progressBar?: HTMLElement | null): void {\n // Clear interval first\n if (this.progressUpdateInterval) {\n clearInterval(this.progressUpdateInterval);\n this.progressUpdateInterval = null;\n }\n \n // Stop and clear audio\n if (this.currentAudio) {\n this.currentAudio.pause();\n this.currentAudio.currentTime = 0;\n this.currentAudio = null;\n }\n \n // Reset button state\n if (this.currentButton) {\n this.setButtonState(this.currentButton, 'ready');\n this.currentButton = null;\n }\n \n // Clean up progress bar immediately\n if (progressBar) {\n progressBar.classList.remove('playing');\n const slider = progressBar.querySelector<HTMLInputElement>('.supernal-tts-progress-slider');\n if (slider) {\n slider.value = '0';\n }\n const timeDisplay = progressBar.querySelector('.supernal-tts-time-display');\n if (timeDisplay) {\n timeDisplay.innerHTML = '<span class=\"current\">0:00</span> / <span class=\"duration\">0:00</span>';\n }\n }\n \n this.isProcessing = false;\n }\n\n /**\n * Clean stop audio but keep progress bar at current position (for voice/speed changes)\n */\n private cleanStopAudioKeepProgress(progressBar?: HTMLElement | null): void {\n // Clear any existing progress update intervals first\n if (this.progressUpdateInterval) {\n clearInterval(this.progressUpdateInterval);\n this.progressUpdateInterval = null;\n }\n \n // Stop and clear audio\n if (this.currentAudio) {\n this.currentAudio.pause();\n this.currentAudio.currentTime = 0;\n this.currentAudio = null;\n }\n \n // Reset button state but don't clear currentButton yet (we're regenerating)\n if (this.currentButton) {\n this.setButtonState(this.currentButton, 'loading');\n }\n \n // Keep progress bar visible but stop updates\n // Progress position is maintained by not resetting the slider value\n \n this.isProcessing = false;\n }\n\n /**\n * Handle play click and seek to a specific position (used for voice/speed changes)\n */\n private async handlePlayClickFromPosition(button: HTMLButtonElement, text: string, options: any, positionPercent: number): Promise<void> {\n const { hash, progressBar } = options;\n \n // Don't allow starting new audio while processing\n if (this.isProcessing) {\n return;\n }\n\n try {\n this.isProcessing = true;\n \n // Update button state\n this.setButtonState(button, 'loading');\n \n // Check cache first\n let audioUrl = this.getCachedAudioUrl(hash);\n \n if (!audioUrl) {\n // Generate new audio\n const response = await this.generateAudio(text, options);\n // Convert relative audioUrl to absolute URL\n audioUrl = response.audioUrl.startsWith('http') \n ? response.audioUrl \n : `${this.apiUrl}${response.audioUrl}`;\n \n // Cache the result\n this.cacheAudioUrl(hash, audioUrl, response);\n }\n \n // Play audio with progress bar updates and seek to saved position\n await this.playAudioFromPosition(audioUrl, button, progressBar, positionPercent);\n \n } catch (error) {\n console.error('TTS Error:', error);\n this.setButtonState(button, 'error');\n \n // Show user-friendly error\n this.showError(button, (error as Error).message);\n \n setTimeout(() => this.setButtonState(button, 'ready'), 3000);\n this.isProcessing = false;\n }\n }\n\n /**\n * Play audio starting from a specific position\n */\n private async playAudioFromPosition(audioUrl: string, button: HTMLButtonElement, progressBar: HTMLElement | null | undefined, positionPercent: number): Promise<void> {\n return new Promise((resolve, reject) => {\n const audio = new Audio(audioUrl);\n this.currentAudio = audio;\n this.currentButton = button;\n \n // Enable pitch preservation for client-side speed control\n // This uses native browser time-stretching to maintain pitch quality\n if (this.clientSideSpeed) {\n audio.preservesPitch = true;\n // Fallback for older browsers\n (audio as any).mozPreservesPitch = true;\n (audio as any).webkitPreservesPitch = true;\n }\n \n // Clear any existing interval before starting new one\n if (this.progressUpdateInterval) {\n clearInterval(this.progressUpdateInterval);\n this.progressUpdateInterval = null;\n }\n \n let hasSeekCompleted = false;\n \n audio.addEventListener('loadstart', () => {\n if (this.currentAudio === audio) {\n this.setButtonState(button, 'loading');\n }\n });\n \n audio.addEventListener('loadedmetadata', () => {\n // Once we know the duration, seek to the saved position\n if (this.currentAudio === audio && !hasSeekCompleted && positionPercent > 0) {\n audio.currentTime = audio.duration * positionPercent;\n hasSeekCompleted = true;\n }\n });\n \n audio.addEventListener('canplay', () => {\n if (this.currentAudio === audio) {\n // Seek again if metadata wasn't ready earlier\n if (!hasSeekCompleted && positionPercent > 0 && audio.duration > 0) {\n audio.currentTime = audio.duration * positionPercent;\n hasSeekCompleted = true;\n }\n \n this.setButtonState(button, 'playing');\n this.isProcessing = false;\n const playPromise = audio.play();\n if (playPromise !== undefined) {\n playPromise.catch(error => {\n console.error('Audio play failed:', error);\n if (this.currentAudio === audio) {\n this.setButtonState(button, 'error');\n this.currentAudio = null;\n this.currentButton = null;\n this.isProcessing = false;\n reject(new Error('Audio playback failed'));\n }\n });\n }\n \n // Start progress bar updates using global interval\n if (progressBar) {\n progressBar.classList.add('playing');\n this.progressUpdateInterval = window.setInterval(() => {\n this.updateProgressBar(audio, progressBar);\n }, 100);\n }\n }\n });\n \n audio.addEventListener('ended', () => {\n if (this.currentAudio === audio) {\n this.setButtonState(button, 'ready');\n this.currentAudio = null;\n this.currentButton = null;\n \n // Clear progress bar updates\n if (this.progressUpdateInterval) {\n clearInterval(this.progressUpdateInterval);\n this.progressUpdateInterval = null;\n }\n if (progressBar) {\n progressBar.classList.remove('playing');\n // Reset progress bar\n const slider = progressBar.querySelector<HTMLInputElement>('.supernal-tts-progress-slider');\n if (slider) slider.value = '0';\n const timeDisplay = progressBar.querySelector('.supernal-tts-time-display');\n if (timeDisplay) timeDisplay.innerHTML = '<span class=\"current\">0:00</span> / <span class=\"duration\">0:00</span>';\n }\n \n resolve();\n }\n });\n \n audio.addEventListener('error', (e) => {\n if (this.currentAudio === audio) {\n this.setButtonState(button, 'error');\n this.currentAudio = null;\n this.currentButton = null;\n \n // Clear progress bar updates\n if (this.progressUpdateInterval) {\n clearInterval(this.progressUpdateInterval);\n this.progressUpdateInterval = null;\n }\n if (progressBar) {\n progressBar.classList.remove('playing');\n }\n \n reject(new Error('Audio playback failed'));\n }\n });\n \n audio.load();\n });\n }\n\n private setButtonState(button: HTMLButtonElement, state: string): void {\n button.classList.remove('supernal-tts-loading', 'supernal-tts-playing', 'supernal-tts-paused', 'supernal-tts-error');\n \n const playIcon = button.querySelector<SVGElement>('.supernal-tts-play-icon');\n const text = button.querySelector<HTMLSpanElement>('.supernal-tts-text');\n \n switch (state) {\n case 'loading':\n button.classList.add('supernal-tts-loading');\n button.disabled = true;\n // Loading spinner shown via CSS\n if (text) text.textContent = 'Loading...';\n break;\n \n case 'playing':\n button.classList.add('supernal-tts-playing');\n button.disabled = false;\n if (playIcon) playIcon.innerHTML = '<rect x=\"6\" y=\"4\" width=\"4\" height=\"16\"/><rect x=\"14\" y=\"4\" width=\"4\" height=\"16\"/>';\n if (text) text.textContent = 'Pause';\n break;\n \n case 'paused':\n button.classList.add('supernal-tts-paused');\n button.disabled = false;\n if (playIcon) playIcon.innerHTML = '<path d=\"M8 5v14l11-7z\"/>';\n if (text) text.textContent = 'Resume';\n break;\n \n case 'error':\n button.classList.add('supernal-tts-error');\n button.disabled = false;\n if (playIcon) playIcon.innerHTML = '<path d=\"M12 2L2 7v10c0 5.55 3.84 9.95 9 11 5.16-1.05 9-5.45 9-11V7l-10-5z\"/>';\n if (text) text.textContent = 'Error';\n break;\n \n default: // ready\n button.disabled = false;\n if (playIcon) playIcon.innerHTML = '<path d=\"M8 5v14l11-7z\"/>';\n if (text) text.textContent = 'Listen';\n break;\n }\n }\n\n private showError(button: HTMLButtonElement, message: string): void {\n const tooltip = document.createElement('div');\n tooltip.className = 'supernal-tts-error-tooltip';\n \n if (message.includes('development mode')) {\n tooltip.textContent = message;\n } else if (message.includes('Failed to fetch')) {\n tooltip.textContent = 'Service unavailable (dev mode)';\n } else {\n tooltip.textContent = 'Audio generation failed';\n }\n \n button.parentNode?.appendChild(tooltip);\n \n setTimeout(() => {\n if (tooltip.parentNode) {\n tooltip.parentNode.removeChild(tooltip);\n }\n }, 3000);\n }\n\n private generateHash(text: string, options: any = {}): string {\n const str = text + JSON.stringify(options);\n let hash = 0;\n \n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n \n return Math.abs(hash).toString(36);\n }\n\n private getCachedAudioUrl(hash: string): string | null {\n const cached = this.cache.get(hash);\n \n if (!cached) return null;\n \n if (Date.now() > cached.expiry) {\n this.cache.delete(hash);\n this.saveCacheToStorage();\n return null;\n }\n \n const audioUrl = cached.audioUrl;\n return audioUrl.startsWith('http') \n ? audioUrl \n : `${this.apiUrl}${audioUrl}`;\n }\n\n private cacheAudioUrl(hash: string, audioUrl: string, metadata: any = {}): void {\n this.cache.set(hash, {\n audioUrl,\n metadata,\n expiry: Date.now() + this.cacheExpiry,\n timestamp: Date.now()\n });\n \n this.saveCacheToStorage();\n }\n\n private loadCacheFromStorage(): void {\n try {\n const stored = localStorage.getItem('supernal-tts-cache');\n if (stored) {\n const data = JSON.parse(stored);\n this.cache = new Map(Object.entries(data));\n }\n } catch (error) {\n console.warn('Failed to load TTS cache:', error);\n }\n }\n\n private saveCacheToStorage(): void {\n try {\n const data = Object.fromEntries(this.cache);\n localStorage.setItem('supernal-tts-cache', JSON.stringify(data));\n } catch (error) {\n console.warn('Failed to save TTS cache:', error);\n }\n }\n\n // Public API methods\n public static init(options?: SupernalTTSOptions): SupernalTTS {\n if ((window as any).SupernalTTSInstance) {\n console.debug('SupernalTTS already initialized, returning existing instance');\n return (window as any).SupernalTTSInstance;\n }\n \n (window as any).SupernalTTSInstance = new SupernalTTS(options);\n return (window as any).SupernalTTSInstance;\n }\n\n public static getInstance(): SupernalTTS | undefined {\n return (window as any).SupernalTTSInstance;\n }\n\n public addWidget(element: HTMLElement | string, text: string, options: any = {}): void {\n let el: HTMLElement | null = null;\n \n if (typeof element === 'string') {\n el = document.querySelector<HTMLElement>(element);\n } else {\n el = element;\n }\n \n if (!el) {\n console.error('TTS Widget: Element not found');\n return;\n }\n \n el.classList.add('supernal-tts-widget');\n el.dataset.text = text;\n \n if (options.voice) el.dataset.voice = options.voice;\n if (options.provider) el.dataset.provider = options.provider;\n \n this.setupWidget(el);\n }\n}\n\n// Auto-initialize if configuration is found\nif (typeof window !== 'undefined') {\n if (!(window as any).SupernalTTS) {\n (window as any).SupernalTTS = SupernalTTS;\n \n const autoInitScript = document.querySelector<HTMLScriptElement>('script[data-supernal-tts-auto-init]');\n if (autoInitScript && autoInitScript.dataset) {\n const config = JSON.parse(autoInitScript.dataset.supernalTtsAutoInit || '{}');\n SupernalTTS.init(config);\n }\n } else {\n console.debug('SupernalTTS already loaded, skipping redeclaration');\n }\n}\n\nexport default SupernalTTS;\n\n", "/**\n * Smart Widget Loader\n *\n * Dynamically loads the latest compatible widget version from unpkg CDN\n * with automatic fallback to bundled version for CSP/firewall scenarios.\n *\n * @example\n * ```typescript\n * import { WidgetLoader } from '@supernal-tts/widget';\n *\n * // Default: CDN @1 with fallback\n * const widget = await WidgetLoader.load();\n *\n * // Force bundled (CSP-safe)\n * const widget = await WidgetLoader.load({ mode: 'bundled' });\n *\n * // Pin specific version\n * const widget = await WidgetLoader.load({ version: '1.2.1' });\n * ```\n */\n\nexport interface LoaderOptions {\n /**\n * Version strategy:\n * - 'major': Load latest v1.x.x (default, recommended)\n * - 'latest': Load absolute latest (risky, may break)\n * - '1.2.1': Pin to specific version\n */\n version?: 'latest' | 'major' | string;\n\n /**\n * Loading mode:\n * - 'auto': Try CDN, fallback to bundled (default)\n * - 'cdn': Force CDN load, error if unavailable\n * - 'bundled': Always use bundled version (CSP-safe)\n */\n mode?: 'cdn' | 'bundled' | 'auto';\n\n /**\n * Custom CDN URL (default: https://unpkg.com/@supernal/tts-widget)\n */\n cdnUrl?: string;\n\n /**\n * CDN fetch timeout in milliseconds (default: 3000)\n */\n timeout?: number;\n\n /**\n * Callback when CDN load fails and fallback is used\n */\n onCdnFail?: (error: Error) => void;\n\n /**\n * Callback when CDN load succeeds\n */\n onCdnSuccess?: () => void;\n}\n\nexport interface SupernalTTSModule {\n SupernalTTS: any;\n}\n\n// Package version injected at build time\ndeclare const __PACKAGE_VERSION__: string;\n\nexport class WidgetLoader {\n private static instance: any = null;\n private static loadPromise: Promise<any> | null = null;\n\n /**\n * Load the Supernal TTS widget with smart CDN loading and fallback\n */\n static async load(options: LoaderOptions = {}): Promise<any> {\n // Return cached instance if already loaded\n if (this.instance) {\n return this.instance;\n }\n\n // Return existing load promise if already loading\n if (this.loadPromise) {\n return this.loadPromise;\n }\n\n const {\n version = 'major',\n mode = 'auto',\n cdnUrl = 'https://unpkg.com/@supernal/tts-widget',\n timeout = 3000,\n onCdnFail,\n onCdnSuccess\n } = options;\n\n // Start loading process\n this.loadPromise = this.loadWidget({\n version,\n mode,\n cdnUrl,\n timeout,\n onCdnFail,\n onCdnSuccess\n });\n\n return this.loadPromise;\n }\n\n private static async loadWidget(options: {\n mode: 'cdn' | 'bundled' | 'auto';\n version: string;\n cdnUrl: string;\n timeout: number;\n onCdnFail?: (error: Error) => void;\n onCdnSuccess?: () => void;\n }): Promise<any> {\n const { mode, version, cdnUrl, timeout, onCdnFail, onCdnSuccess } = options;\n\n // Force bundled mode (CSP-safe, offline-safe)\n if (mode === 'bundled') {\n console.log('[TTS Widget] Loading bundled version (mode: bundled)');\n return this.loadBundled();\n }\n\n // Try CDN first (mode === 'cdn' or 'auto')\n if (mode === 'cdn' || mode === 'auto') {\n try {\n console.log('[TTS Widget] Attempting CDN load...');\n const widget = await this.loadFromCdn(cdnUrl, version, timeout);\n\n console.log('[TTS Widget] CDN load successful');\n onCdnSuccess?.();\n\n this.instance = widget;\n return widget;\n\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.warn('[TTS Widget] CDN load failed:', errorMessage);\n\n onCdnFail?.(error instanceof Error ? error : new Error(errorMessage));\n\n // In 'cdn' mode, throw the error (no fallback)\n if (mode === 'cdn') {\n throw new Error(`Failed to load widget from CDN: ${errorMessage}`);\n }\n\n // In 'auto' mode, fallback to bundled\n console.log('[TTS Widget] Falling back to bundled version');\n return this.loadBundled();\n }\n }\n\n // Should never reach here, but fallback to bundled just in case\n return this.loadBundled();\n }\n\n private static async loadFromCdn(\n cdnUrl: string,\n version: string,\n timeout: number\n ): Promise<any> {\n const versionPath = this.resolveVersionPath(version);\n const url = `${cdnUrl}${versionPath}/dist/widget.js`;\n\n console.log(`[TTS Widget] Loading from CDN: ${url}`);\n\n // Create timeout controller\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n // Dynamic import with timeout\n // Note: AbortController support in dynamic import is limited, so we use a race\n const modulePromise = import(/* @vite-ignore */ url);\n const timeoutPromise = new Promise((_, reject) => {\n setTimeout(() => reject(new Error(`CDN load timeout after ${timeout}ms`)), timeout);\n });\n\n const module = await Promise.race([modulePromise, timeoutPromise]) as SupernalTTSModule;\n\n clearTimeout(timeoutId);\n\n if (!module || !module.SupernalTTS) {\n throw new Error('CDN module missing SupernalTTS export');\n }\n\n this.instance = module.SupernalTTS;\n return this.instance;\n\n } catch (error) {\n clearTimeout(timeoutId);\n throw error;\n }\n }\n\n private static async loadBundled(): Promise<any> {\n try {\n // Import bundled widget\n const module = await import('./widget.js') as SupernalTTSModule;\n\n if (!module || !module.SupernalTTS) {\n throw new Error('Bundled widget missing SupernalTTS export');\n }\n\n this.instance = module.SupernalTTS;\n return this.instance;\n\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to load bundled widget: ${errorMessage}`);\n }\n }\n\n private static resolveVersionPath(version: string): string {\n // Major version (e.g., \"1\" from \"1.2.1\")\n if (version === 'major') {\n const majorVersion = this.getMajorVersion();\n return `@${majorVersion}`;\n }\n\n // Always latest (risky)\n if (version === 'latest') {\n return '@latest';\n }\n\n // Specific version (e.g., \"1.2.1\")\n return `@${version}`;\n }\n\n private static getMajorVersion(): string {\n // Try to read from injected package version\n if (typeof __PACKAGE_VERSION__ !== 'undefined') {\n const match = __PACKAGE_VERSION__.match(/^(\\d+)\\./);\n if (match) {\n return match[1];\n }\n }\n\n // Fallback to hardcoded major version\n // This will be replaced by the build process\n return '1';\n }\n\n /**\n * Reset the loader (useful for testing)\n */\n static reset(): void {\n this.instance = null;\n this.loadPromise = null;\n }\n\n /**\n * Get current loaded instance (if any)\n */\n static getInstance(): any | null {\n return this.instance;\n }\n}\n\n// Default export for convenience\nexport default WidgetLoader;\n"],
5
+ "mappings": "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",
6
+ "names": ["widget_exports", "__export", "SupernalTTS", "widget_default", "init_widget", "__esmMin", "_SupernalTTS", "options", "mutations", "mutation", "node", "element", "widget", "widgets", "config", "hash", "controls", "text", "voice", "provider", "speed", "voicesAttr", "voices", "v", "enableSpeed", "enableProgress", "apiKey", "buttonContainer", "playButton", "badge", "e", "container", "topRow", "voiceSelect", "speedControl", "speedSlider", "speedValue", "newHash", "buttonGroup", "stopButton", "progressBar", "progressSlider", "percent", "currentTime", "duration", "currentVoice", "currentSpeed", "savedPosition", "controlsRow", "voiceDropdown", "newVoice", "speedDropdown", "newSpeed", "selectedVoice", "toggleButton", "dropdown", "option", "opt", "hideTimeout", "rect", "viewportHeight", "isHidden", "selectedSpeed", "button", "speeds", "slider", "timeDisplay", "percentage", "seconds", "mins", "secs", "word", "compact", "img", "minimizedBtn", "fullBtn", "addHoverEffect", "btn", "clearCache", "count", "clickX", "btnWidth", "audioUrl", "response", "error", "body", "headers", "data", "originalMessage", "reader", "decoder", "buffer", "totalChunks", "completedChunks", "updateProgress", "completed", "total", "currentSpan", "done", "value", "lines", "line", "event", "parseError", "playbackSpeed", "resolve", "reject", "audio", "playPromise", "currentTimeSpan", "durationSpan", "positionPercent", "hasSeekCompleted", "state", "playIcon", "message", "tooltip", "str", "i", "char", "cached", "metadata", "stored", "el", "autoInitScript", "WidgetLoader", "options", "version", "mode", "cdnUrl", "timeout", "onCdnFail", "onCdnSuccess", "widget", "error", "errorMessage", "versionPath", "url", "controller", "timeoutId", "modulePromise", "timeoutPromise", "_", "reject", "module", "match", "loader_default"]
7
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * TTSInitializer - React component for initializing Supernal TTS widget
3
+ *
4
+ * Usage:
5
+ * import { TTSInitializer } from '@supernal/tts-widget/react';
6
+ * import '@supernal/tts-widget/widget.css';
7
+ *
8
+ * // In your root layout or _app.tsx
9
+ * <TTSInitializer
10
+ * apiUrl="https://www.tts.supernal.ai"
11
+ * apiKey={process.env.NEXT_PUBLIC_TTS_API_KEY}
12
+ * mode="auto" // Try CDN, fallback to bundled (default)
13
+ * version="major" // Load latest v1.x.x (default)
14
+ * />
15
+ */
16
+ import * as React from 'react';
17
+ export interface TTSInitializerProps {
18
+ apiUrl?: string;
19
+ apiKey?: string;
20
+ provider?: string;
21
+ voice?: string;
22
+ speed?: number;
23
+ devMode?: boolean;
24
+ clientSideSpeed?: boolean;
25
+ showBranding?: boolean;
26
+ /**
27
+ * Loading mode:
28
+ * - 'auto': Try CDN, fallback to bundled (default, recommended)
29
+ * - 'cdn': Force CDN load, error if unavailable
30
+ * - 'bundled': Always use bundled version (CSP-safe)
31
+ */
32
+ mode?: 'auto' | 'cdn' | 'bundled';
33
+ /**
34
+ * Version strategy:
35
+ * - 'major': Load latest v1.x.x (default, recommended)
36
+ * - 'latest': Load absolute latest (risky)
37
+ * - '1.3.0': Pin to specific version
38
+ */
39
+ version?: 'major' | 'latest' | string;
40
+ /**
41
+ * CDN fetch timeout in milliseconds (default: 3000)
42
+ */
43
+ timeout?: number;
44
+ /**
45
+ * Callback when CDN load fails and fallback is used
46
+ */
47
+ onCdnFail?: (error: Error) => void;
48
+ /**
49
+ * Callback when CDN load succeeds
50
+ */
51
+ onCdnSuccess?: () => void;
52
+ }
53
+ export default function TTSInitializer({ apiUrl, apiKey, provider, voice, speed, devMode, clientSideSpeed, showBranding, mode, version, timeout, onCdnFail, onCdnSuccess, }: TTSInitializerProps): React.DetailedReactHTMLElement<{
54
+ style: {
55
+ padding: string;
56
+ background: string;
57
+ border: string;
58
+ borderRadius: string;
59
+ color: "#c33";
60
+ fontFamily: "monospace";
61
+ fontSize: string;
62
+ };
63
+ }, HTMLElement> | null;
64
+ //# sourceMappingURL=TTSInitializer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TTSInitializer.d.ts","sourceRoot":"","sources":["../../src/react/TTSInitializer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;IAGvB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;IAElC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;IAEtC;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEnC;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,EACrC,MAAsC,EACtC,MAAM,EACN,QAAmB,EACnB,KAAe,EACf,KAAW,EACX,OAAO,EACP,eAAsB,EACtB,YAAmB,EACnB,IAAa,EACb,OAAiB,EACjB,OAAc,EACd,SAAS,EACT,YAAY,GACb,EAAE,mBAAmB;;;;;;;;;;uBAqFrB"}
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ /**
3
+ * TTSInitializer - React component for initializing Supernal TTS widget
4
+ *
5
+ * Usage:
6
+ * import { TTSInitializer } from '@supernal/tts-widget/react';
7
+ * import '@supernal/tts-widget/widget.css';
8
+ *
9
+ * // In your root layout or _app.tsx
10
+ * <TTSInitializer
11
+ * apiUrl="https://www.tts.supernal.ai"
12
+ * apiKey={process.env.NEXT_PUBLIC_TTS_API_KEY}
13
+ * mode="auto" // Try CDN, fallback to bundled (default)
14
+ * version="major" // Load latest v1.x.x (default)
15
+ * />
16
+ */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.default = TTSInitializer;
52
+ const React = __importStar(require("react"));
53
+ const react_1 = require("react");
54
+ function TTSInitializer({ apiUrl = 'https://www.tts.supernal.ai', apiKey, provider = 'openai', voice = 'alloy', speed = 1.0, devMode, clientSideSpeed = true, showBranding = true, mode = 'auto', version = 'major', timeout = 3000, onCdnFail, onCdnSuccess, }) {
55
+ const [error, setError] = (0, react_1.useState)(null);
56
+ (0, react_1.useEffect)(() => {
57
+ // Only run on client side
58
+ if (typeof window === 'undefined')
59
+ return;
60
+ let isMounted = true;
61
+ // Note: Users should import CSS separately:
62
+ // import '@supernal/tts-widget/widget.css';
63
+ // Load widget using smart loader
64
+ const loadWidget = async () => {
65
+ try {
66
+ const loaderModule = await Promise.resolve().then(() => __importStar(require('../loader')));
67
+ const { WidgetLoader } = loaderModule;
68
+ const widget = await WidgetLoader.load({
69
+ mode,
70
+ version,
71
+ timeout,
72
+ onCdnFail: (err) => {
73
+ console.warn('[TTS Widget] CDN load failed, using bundled version:', err.message);
74
+ onCdnFail?.(err);
75
+ },
76
+ onCdnSuccess: () => {
77
+ console.log('[TTS Widget] Successfully loaded from CDN');
78
+ onCdnSuccess?.();
79
+ },
80
+ });
81
+ // Only initialize if component is still mounted
82
+ if (!isMounted)
83
+ return;
84
+ // Initialize widget if not already initialized
85
+ if (!window.SupernalTTSInstance) {
86
+ widget.init({
87
+ apiUrl,
88
+ apiKey,
89
+ provider,
90
+ voice,
91
+ speed,
92
+ devMode,
93
+ clientSideSpeed,
94
+ showBranding,
95
+ });
96
+ }
97
+ }
98
+ catch (err) {
99
+ if (!isMounted)
100
+ return;
101
+ const errorMessage = err instanceof Error ? err.message : String(err);
102
+ console.error('[TTS Widget] Failed to load:', errorMessage);
103
+ setError(errorMessage);
104
+ }
105
+ };
106
+ loadWidget();
107
+ // Cleanup function
108
+ return () => {
109
+ isMounted = false;
110
+ // Don't remove CSS or instance - they should persist across component unmounts
111
+ };
112
+ }, [apiUrl, apiKey, provider, voice, speed, devMode, clientSideSpeed, showBranding, mode, version, timeout]);
113
+ // Optionally render error state
114
+ if (error && devMode) {
115
+ return React.createElement('div', {
116
+ style: {
117
+ padding: '12px',
118
+ background: '#fee',
119
+ border: '1px solid #fcc',
120
+ borderRadius: '4px',
121
+ color: '#c33',
122
+ fontFamily: 'monospace',
123
+ fontSize: '12px',
124
+ }
125
+ }, [
126
+ React.createElement('strong', { key: 'title' }, 'TTS Widget Error: '),
127
+ error
128
+ ]);
129
+ }
130
+ return null; // This component doesn't render anything in production
131
+ }
132
+ //# sourceMappingURL=TTSInitializer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TTSInitializer.js","sourceRoot":"","sources":["../../src/react/TTSInitializer.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Supernal TTS Widget - React Components
3
+ *
4
+ * Provides React-specific components for easy integration
5
+ */
6
+ export { default as TTSInitializer, type TTSInitializerProps } from './TTSInitializer';
7
+ export { default } from './TTSInitializer';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAGvF,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ /**
3
+ * Supernal TTS Widget - React Components
4
+ *
5
+ * Provides React-specific components for easy integration
6
+ */
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.default = exports.TTSInitializer = void 0;
12
+ var TTSInitializer_1 = require("./TTSInitializer");
13
+ Object.defineProperty(exports, "TTSInitializer", { enumerable: true, get: function () { return __importDefault(TTSInitializer_1).default; } });
14
+ // Also provide default export for convenience
15
+ var TTSInitializer_2 = require("./TTSInitializer");
16
+ Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(TTSInitializer_2).default; } });
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;;;;AAEH,mDAAuF;AAA9E,iIAAA,OAAO,OAAkB;AAElC,8CAA8C;AAC9C,mDAA2C;AAAlC,0HAAA,OAAO,OAAA"}