@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,176 @@
1
+ /**
2
+ * Supernal TTS Widget
3
+ * Embeddable text-to-speech widget for blogs and websites
4
+ */
5
+ export interface SupernalTTSOptions {
6
+ apiUrl?: string;
7
+ voice?: string;
8
+ provider?: string;
9
+ speed?: number;
10
+ autoHash?: boolean;
11
+ cacheExpiry?: number;
12
+ apiKey?: string;
13
+ showBranding?: boolean;
14
+ version?: string;
15
+ devMode?: boolean;
16
+ clientSideSpeed?: boolean;
17
+ generationMode?: 'standard' | 'progressive' | 'realtime';
18
+ progressiveThreshold?: number;
19
+ }
20
+ export interface TTSGenerateOptions {
21
+ provider: string;
22
+ voice: string;
23
+ speed: number;
24
+ format?: string;
25
+ quality?: string;
26
+ cache?: boolean;
27
+ }
28
+ export interface TTSResponse {
29
+ audioUrl: string;
30
+ hash: string;
31
+ cached: boolean;
32
+ duration?: number;
33
+ cost?: number;
34
+ }
35
+ export interface CachedAudio {
36
+ audioUrl: string;
37
+ metadata: Record<string, any>;
38
+ expiry: number;
39
+ timestamp: number;
40
+ }
41
+ export interface WidgetData {
42
+ text: string;
43
+ voice: string;
44
+ provider: string;
45
+ speed: number;
46
+ hash: string;
47
+ playButton: HTMLButtonElement;
48
+ stopButton?: HTMLButtonElement;
49
+ voiceSelect?: HTMLSelectElement;
50
+ speedSlider?: HTMLInputElement;
51
+ progressSlider?: HTMLInputElement;
52
+ advanced?: boolean;
53
+ }
54
+ export interface WidgetConfig {
55
+ text: string;
56
+ voices: string[];
57
+ enableSpeed: boolean;
58
+ enableProgress: boolean;
59
+ speed: number;
60
+ provider: string;
61
+ voice: string;
62
+ apiKey?: string;
63
+ }
64
+ export declare class SupernalTTS {
65
+ private apiUrl;
66
+ private defaultVoice;
67
+ private defaultProvider;
68
+ private defaultSpeed;
69
+ private autoHash;
70
+ private cacheExpiry;
71
+ private apiKey?;
72
+ private showBranding;
73
+ private version;
74
+ private devMode;
75
+ private clientSideSpeed;
76
+ private generationMode;
77
+ private progressiveThreshold;
78
+ private cache;
79
+ private currentAudio;
80
+ private currentButton;
81
+ private currentWidget?;
82
+ private isProcessing;
83
+ private lastClickTime;
84
+ private debounceDelay;
85
+ private progressUpdateInterval;
86
+ private audioQueue;
87
+ constructor(options?: SupernalTTSOptions);
88
+ private init;
89
+ private observeDOM;
90
+ private initializeWidgets;
91
+ private setupWidget;
92
+ /**
93
+ * Parse widget configuration from data attributes
94
+ */
95
+ private parseWidgetConfig;
96
+ private setupBasicWidget;
97
+ private setupCompactWidget;
98
+ private setupAdvancedWidget;
99
+ /**
100
+ * Setup modular widget with conditional features based on config
101
+ */
102
+ private setupModularWidget;
103
+ /**
104
+ * Create voice dropdown with person icon button
105
+ */
106
+ private createVoiceDropdown;
107
+ /**
108
+ * Create speed dropdown with preset values (hover to reveal)
109
+ */
110
+ private createSpeedDropdown;
111
+ /**
112
+ * Create seekable progress bar with time display
113
+ */
114
+ private createProgressBar;
115
+ /**
116
+ * Format seconds to MM:SS
117
+ */
118
+ private formatTime;
119
+ /**
120
+ * Format voice name for display
121
+ */
122
+ private formatVoiceName;
123
+ private createStopButton;
124
+ private createPlayButton;
125
+ private createBrandingBadge;
126
+ /**
127
+ * Dev Mode Cache Clear Button
128
+ * Adds a minimizable floating button in dev mode to clear local cache
129
+ * Only appears on pages that have TTS widgets
130
+ */
131
+ private addDevModeClearButton;
132
+ private handlePlayClick;
133
+ private generateAudio;
134
+ /**
135
+ * Generate audio using progressive SSE streaming
136
+ * Updates progress bar in real-time as chunks complete
137
+ */
138
+ private generateAudioProgressive;
139
+ private playAudio;
140
+ /**
141
+ * Update progress bar during playback
142
+ */
143
+ private updateProgressBar;
144
+ private pauseAudio;
145
+ private resumeAudio;
146
+ private stopAudio;
147
+ /**
148
+ * Clean stop audio with proper progress bar cleanup
149
+ * Used when regenerating audio with different parameters
150
+ */
151
+ private cleanStopAudio;
152
+ /**
153
+ * Clean stop audio but keep progress bar at current position (for voice/speed changes)
154
+ */
155
+ private cleanStopAudioKeepProgress;
156
+ /**
157
+ * Handle play click and seek to a specific position (used for voice/speed changes)
158
+ */
159
+ private handlePlayClickFromPosition;
160
+ /**
161
+ * Play audio starting from a specific position
162
+ */
163
+ private playAudioFromPosition;
164
+ private setButtonState;
165
+ private showError;
166
+ private generateHash;
167
+ private getCachedAudioUrl;
168
+ private cacheAudioUrl;
169
+ private loadCacheFromStorage;
170
+ private saveCacheToStorage;
171
+ static init(options?: SupernalTTSOptions): SupernalTTS;
172
+ static getInstance(): SupernalTTS | undefined;
173
+ addWidget(element: HTMLElement | string, text: string, options?: any): void;
174
+ }
175
+ export default SupernalTTS;
176
+ //# sourceMappingURL=widget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"widget.d.ts","sourceRoot":"","sources":["../src/widget.ts"],"names":[],"mappings":"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"}
package/dist/widget.js ADDED
@@ -0,0 +1,96 @@
1
+ var L=class w{constructor(e={}){this.currentAudio=null;this.currentButton=null;this.isProcessing=!1;this.lastClickTime=0;this.debounceDelay=300;this.progressUpdateInterval=null;this.audioQueue=[];this.apiUrl=e.apiUrl||"https://www.tts.supernal.ai",this.defaultVoice=e.voice||"default",this.defaultProvider=e.provider||"openai",this.defaultSpeed=e.speed||1,this.autoHash=e.autoHash!==!1,this.cacheExpiry=e.cacheExpiry||7*24*60*60*1e3,this.apiKey=e.apiKey,console.log("[TTS Widget] Constructor - API Key provided:",!!e.apiKey),console.log("[TTS Widget] Constructor - API Key saved:",!!this.apiKey),this.showBranding=e.showBranding!==!1,this.version=e.version||"1.0.0",this.devMode=e.devMode===!0,this.clientSideSpeed=e.clientSideSpeed!==!1,this.generationMode=e.generationMode||"standard",this.progressiveThreshold=e.progressiveThreshold||2e3,this.cache=new Map,this.loadCacheFromStorage(),this.init()}init(){document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>this.initializeWidgets()):this.initializeWidgets(),this.observeDOM()}observeDOM(){new MutationObserver(t=>{t.forEach(s=>{s.addedNodes.forEach(r=>{if(r.nodeType===Node.ELEMENT_NODE){let o=r;o.classList?.contains("supernal-tts-widget")?this.setupWidget(o):o.querySelectorAll?.(".supernal-tts-widget")?.forEach(i=>this.setupWidget(i))}})})}).observe(document.body,{childList:!0,subtree:!0})}initializeWidgets(){let e=document.querySelectorAll(".supernal-tts-widget");e.forEach(t=>this.setupWidget(t)),this.devMode&&e.length>0&&this.addDevModeClearButton()}setupWidget(e){if(e.classList.contains("supernal-tts-widget-initialized"))return;let t=this.parseWidgetConfig(e);if(!t.text){console.warn("TTS Widget: No text found for widget",e);return}let s=this.generateHash(t.text,{voice:t.voice,provider:t.provider,speed:t.speed}),r=e.dataset.controls;if(r==="advanced"||r==="true"){this.setupAdvancedWidget(e,t.text,{voice:t.voice,provider:t.provider,speed:t.speed,hash:s,apiKey:t.apiKey});return}else if(r==="compact"){this.setupCompactWidget(e,t.text,{voice:t.voice,provider:t.provider,speed:t.speed,hash:s,apiKey:t.apiKey});return}this.setupModularWidget(e,t,s)}parseWidgetConfig(e){let t=e.dataset.text||e.textContent?.trim()||"",s=e.dataset.voice||this.defaultVoice,r=e.dataset.provider||this.defaultProvider,o=e.dataset.speed?parseFloat(e.dataset.speed):this.defaultSpeed,n=e.dataset.voices||"",i=n?n.split(",").map(h=>h.trim()).filter(Boolean):[],a=e.dataset.enableSpeed==="true",l=e.dataset.enableProgress==="true",d=e.dataset.apiKey||this.apiKey;return{text:t,voices:i,enableSpeed:a,enableProgress:l,speed:o,provider:r,voice:s,apiKey:d}}setupBasicWidget(e,t,s){let{voice:r,provider:o,speed:n,hash:i,apiKey:a}=s;if(e.classList.contains("supernal-tts-widget-initialized"))return;let l=document.createElement("div");l.className="supernal-tts-button-container";let d=e.querySelector(".supernal-tts-play");if(d||(d=this.createPlayButton()),l.appendChild(d),this.showBranding&&!e.querySelector(".supernal-badge")){let h=this.createBrandingBadge();l.appendChild(h)}e.appendChild(l),e.classList.add("supernal-tts-widget-initialized"),d.addEventListener("click",h=>{h.preventDefault(),this.handlePlayClick(d,t,{voice:r,provider:o,speed:n,hash:i,apiKey:a})}),e._ttsWidget={text:t,voice:r,provider:o,speed:n,hash:i,playButton:d}}setupCompactWidget(e,t,s){let{hash:r,apiKey:o}=s,{voice:n,provider:i,speed:a}=s;if(e.classList.contains("supernal-tts-widget-initialized"))return;let l=document.createElement("div");l.className="supernal-tts-compact-widget";let d=document.createElement("div");d.className="supernal-tts-top-row";let h=this.createPlayButton(),g=document.createElement("select");if(g.className="supernal-tts-voice-select",g.innerHTML=`
2
+ <option value="alloy" ${n==="alloy"?"selected":""}>Alloy</option>
3
+ <option value="echo" ${n==="echo"?"selected":""}>Echo</option>
4
+ <option value="fable" ${n==="fable"?"selected":""}>Fable</option>
5
+ <option value="onyx" ${n==="onyx"?"selected":""}>Onyx</option>
6
+ <option value="nova" ${n==="nova"?"selected":""}>Nova</option>
7
+ <option value="shimmer" ${n==="shimmer"?"selected":""}>Shimmer</option>
8
+ <option value="coral" ${n==="coral"?"selected":""}>Coral</option>
9
+ `,d.appendChild(h),d.appendChild(g),this.showBranding){let m=this.createBrandingBadge();d.appendChild(m)}let v=document.createElement("div");v.className="supernal-tts-speed-control",v.innerHTML=`
10
+ <label>Speed: <span class="supernal-tts-speed-value">${a}x</span></label>
11
+ <input type="range" class="supernal-tts-speed-slider" min="0.25" max="4.0" step="0.25" value="${a}">
12
+ `,l.appendChild(d),l.appendChild(v),e.innerHTML="",e.appendChild(l),e.classList.add("supernal-tts-widget-initialized","supernal-tts-widget-compact");let f=v.querySelector(".supernal-tts-speed-slider"),c=v.querySelector(".supernal-tts-speed-value");h.addEventListener("click",m=>{m.preventDefault(),a=parseFloat(f.value),n=g.value;let T=this.generateHash(t,{voice:n,provider:i,speed:a});this.handlePlayClick(h,t,{voice:n,provider:i,speed:a,hash:T,apiKey:o})}),f.addEventListener("input",m=>{a=parseFloat(m.target.value),c.textContent=`${a}x`}),g.addEventListener("change",()=>{n=g.value}),e._ttsWidget={text:t,voice:n,provider:i,speed:a,hash:r,playButton:h,compact:!0}}setupAdvancedWidget(e,t,s){let{hash:r,apiKey:o}=s,{voice:n,provider:i,speed:a}=s,l=document.createElement("div");l.className="supernal-tts-advanced-widget";let d=document.createElement("div");d.className="supernal-tts-button-group";let h=this.createPlayButton(),g=this.createStopButton();d.appendChild(h),d.appendChild(g);let v=document.createElement("select");v.className="supernal-tts-voice-select",v.innerHTML=`
13
+ <option value="alloy" ${n==="alloy"?"selected":""}>Alloy</option>
14
+ <option value="echo" ${n==="echo"?"selected":""}>Echo</option>
15
+ <option value="fable" ${n==="fable"?"selected":""}>Fable</option>
16
+ <option value="onyx" ${n==="onyx"?"selected":""}>Onyx</option>
17
+ <option value="nova" ${n==="nova"?"selected":""}>Nova</option>
18
+ <option value="shimmer" ${n==="shimmer"?"selected":""}>Shimmer</option>
19
+ <option value="coral" ${n==="coral"?"selected":""}>Coral</option>
20
+ `;let f=document.createElement("div");f.className="supernal-tts-speed-control",f.innerHTML=`
21
+ <label>Speed: <span class="supernal-tts-speed-value">${a}x</span></label>
22
+ <input type="range" class="supernal-tts-speed-slider" min="0.25" max="4.0" step="0.25" value="${a}">
23
+ `;let c=document.createElement("div");if(c.className="supernal-tts-progress",c.innerHTML=`
24
+ <input type="range" class="supernal-tts-progress-slider" min="0" max="100" value="0" step="0.1">
25
+ <div class="supernal-tts-time"><span class="supernal-tts-current-time">0:00</span> / <span class="supernal-tts-duration">0:00</span></div>
26
+ `,l.appendChild(d),l.appendChild(v),l.appendChild(f),l.appendChild(c),this.showBranding){let p=this.createBrandingBadge();l.appendChild(p)}e.innerHTML="",e.appendChild(l),e.classList.add("supernal-tts-widget-initialized","supernal-tts-widget-advanced");let m=f.querySelector(".supernal-tts-speed-slider"),T=f.querySelector(".supernal-tts-speed-value"),y=c.querySelector(".supernal-tts-progress-slider");h.addEventListener("click",p=>{p.preventDefault(),a=parseFloat(m.value),n=v.value;let S=this.generateHash(t,{voice:n,provider:i,speed:a});this.handlePlayClick(h,t,{voice:n,provider:i,speed:a,hash:S,apiKey:o})}),g.addEventListener("click",p=>{p.preventDefault(),this.stopAudio()}),m.addEventListener("input",p=>{a=parseFloat(p.target.value),T.textContent=`${a}x`}),v.addEventListener("change",()=>{n=v.value}),y.addEventListener("input",p=>{if(this.currentAudio){let S=parseFloat(p.target.value)/100;this.currentAudio.currentTime=this.currentAudio.duration*S}}),setInterval(()=>{if(this.currentAudio&&e._ttsWidget===this.currentWidget){let p=this.currentAudio.currentTime/this.currentAudio.duration*100;y.value=p.toString();let S=this.formatTime(this.currentAudio.currentTime),E=this.formatTime(this.currentAudio.duration);c.querySelector(".supernal-tts-current-time").textContent=S,c.querySelector(".supernal-tts-duration").textContent=E}},100),e._ttsWidget={text:t,voice:n,provider:i,speed:a,hash:r,playButton:h,stopButton:g,voiceSelect:v,speedSlider:m,progressSlider:y,advanced:!0},this.currentWidget=e._ttsWidget}setupModularWidget(e,t,s){let r=t.voice,o=t.speed,{text:n,voices:i,enableSpeed:a,enableProgress:l,provider:d,apiKey:h}=t,g=0,v=document.createElement("div");v.className="supernal-tts-modular-widget";let f=document.createElement("div");f.className="supernal-tts-controls-row";let c=this.createPlayButton(!0);f.appendChild(c);let m=null;i.length>0&&(m=this.createVoiceDropdown(i,r),f.appendChild(m),m.addEventListener("voice-changed",u=>{let p=u.detail.voice;if(p!==r&&this.currentButton===c&&this.currentAudio){r=p,this.currentAudio.duration>0&&(g=this.currentAudio.currentTime/this.currentAudio.duration);let E=e._ttsWidget?.progressBar;this.cleanStopAudioKeepProgress(E),setTimeout(()=>{this.handlePlayClickFromPosition(c,n,{voice:r,provider:d,speed:o,hash:this.generateHash(n,{voice:r,provider:d,speed:o}),progressBar:E,apiKey:h},g)},50)}else r=p}));let T=null;if(a&&(T=this.createSpeedDropdown(o),f.appendChild(T),T.addEventListener("speed-changed",u=>{let p=u.detail.speed;if(p!==o){if(o=p,this.clientSideSpeed&&this.currentAudio&&this.currentButton===c)this.currentAudio.playbackRate=p;else if(!this.clientSideSpeed&&this.currentButton===c&&this.currentAudio){this.currentAudio.duration>0&&(g=this.currentAudio.currentTime/this.currentAudio.duration);let E=e._ttsWidget?.progressBar;this.cleanStopAudioKeepProgress(E),setTimeout(()=>{this.handlePlayClickFromPosition(c,n,{voice:r,provider:d,speed:o,hash:this.generateHash(n,{voice:r,provider:d,speed:o}),progressBar:E,apiKey:h},g)},50)}}})),this.showBranding){let u=this.createBrandingBadge();f.appendChild(u)}v.appendChild(f);let y=null;l&&(y=this.createProgressBar(),v.appendChild(y)),e.insertBefore(v,e.firstChild),e.classList.add("supernal-tts-widget-initialized","supernal-tts-widget-modular"),l&&e.classList.add("has-progress"),c.addEventListener("click",u=>{u.preventDefault();let p=this.generateHash(n,{voice:r,provider:d,speed:o});this.handlePlayClick(c,n,{voice:r,provider:d,speed:o,hash:p,progressBar:y,apiKey:h})}),e._ttsWidget={text:n,voice:r,provider:d,speed:o,hash:s,playButton:c,voiceDropdown:m,speedDropdown:T,progressBar:y}}createVoiceDropdown(e,t){let s=document.createElement("div");s.className="supernal-tts-voice-control";let r=document.createElement("button");r.className="supernal-tts-voice-toggle",r.setAttribute("aria-label","Select voice"),r.setAttribute("aria-expanded","false"),r.innerHTML=`
27
+ <svg class="supernal-tts-voice-icon" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
28
+ <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"/>
29
+ </svg>
30
+ `;let o=document.createElement("div");o.className="supernal-tts-voice-dropdown hidden",o.setAttribute("role","menu"),e.forEach(i=>{let a=document.createElement("button");a.className="supernal-tts-voice-option",a.textContent=this.formatVoiceName(i),a.dataset.voice=i,a.setAttribute("role","menuitem"),i===t&&a.classList.add("active"),a.addEventListener("click",l=>{l.stopPropagation(),l.preventDefault(),o.querySelectorAll(".supernal-tts-voice-option").forEach(d=>{d.classList.remove("active")}),a.classList.add("active"),s.dispatchEvent(new CustomEvent("voice-changed",{detail:{voice:i}}))}),o.appendChild(a)});let n=null;return s.addEventListener("mouseenter",()=>{n&&(clearTimeout(n),n=null),o.classList.remove("hidden"),r.setAttribute("aria-expanded","true"),setTimeout(()=>{let i=o.getBoundingClientRect(),a=window.innerHeight;i.bottom>a&&i.top>i.height?(o.style.bottom="100%",o.style.top="auto"):(o.style.top="calc(100% + 4px)",o.style.bottom="auto")},0)}),s.addEventListener("mouseleave",i=>{n=window.setTimeout(()=>{o.classList.add("hidden"),r.setAttribute("aria-expanded","false")},150)}),o.addEventListener("mouseenter",()=>{n&&(clearTimeout(n),n=null)}),o.addEventListener("mouseleave",()=>{n=window.setTimeout(()=>{o.classList.add("hidden"),r.setAttribute("aria-expanded","false")},150)}),r.addEventListener("click",i=>{i.stopPropagation();let a=o.classList.contains("hidden");o.classList.toggle("hidden"),r.setAttribute("aria-expanded",a?"true":"false")}),s.appendChild(r),s.appendChild(o),s}createSpeedDropdown(e){let t=document.createElement("div");t.className="supernal-tts-speed-control";let s=document.createElement("button");s.className="supernal-tts-speed-toggle",s.setAttribute("aria-label","Adjust playback speed"),s.innerHTML=`
31
+ <svg class="supernal-tts-speed-icon" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
32
+ <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"/>
33
+ <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"/>
34
+ </svg>
35
+ `;let r=document.createElement("div");r.className="supernal-tts-speed-dropdown hidden",r.setAttribute("role","menu");let o=[];for(let i=.6;i<=3;i+=.2)o.push(Math.round(i*10)/10);o.forEach(i=>{let a=document.createElement("button");a.className="supernal-tts-speed-option",a.textContent=`${i.toFixed(1)}x`,a.dataset.speed=i.toString(),a.setAttribute("role","menuitem"),Math.abs(i-e)<.01&&a.classList.add("active"),a.addEventListener("click",l=>{l.stopPropagation(),l.preventDefault(),r.querySelectorAll(".supernal-tts-speed-option").forEach(d=>{d.classList.remove("active")}),a.classList.add("active"),t.dispatchEvent(new CustomEvent("speed-changed",{detail:{speed:i}}))}),r.appendChild(a)});let n=null;return t.addEventListener("mouseenter",()=>{n&&(clearTimeout(n),n=null),r.classList.remove("hidden"),setTimeout(()=>{let i=r.getBoundingClientRect(),a=window.innerHeight;i.bottom>a&&i.top>i.height?(r.style.bottom="100%",r.style.top="auto"):(r.style.top="calc(100% + 4px)",r.style.bottom="auto")},0)}),t.addEventListener("mouseleave",()=>{n=window.setTimeout(()=>{r.classList.add("hidden")},150)}),r.addEventListener("mouseenter",()=>{n&&(clearTimeout(n),n=null)}),r.addEventListener("mouseleave",()=>{n=window.setTimeout(()=>{r.classList.add("hidden")},150)}),t.appendChild(s),t.appendChild(r),t}createProgressBar(){let e=document.createElement("div");e.className="supernal-tts-progress-container";let t=document.createElement("input");t.type="range",t.className="supernal-tts-progress-slider",t.min="0",t.max="100",t.value="0",t.step="0.1",t.setAttribute("aria-label","Seek audio position");let s=document.createElement("div");return s.className="supernal-tts-time-display",s.innerHTML='<span class="current">0:00</span> / <span class="duration">0:00</span>',t.addEventListener("input",r=>{if(this.currentAudio){let o=parseFloat(r.target.value);this.currentAudio.currentTime=o/100*this.currentAudio.duration}}),e.appendChild(t),e.appendChild(s),e}formatTime(e){if(!e||isNaN(e))return"0:00";let t=Math.floor(e/60),s=Math.floor(e%60);return`${t}:${s.toString().padStart(2,"0")}`}formatVoiceName(e){return e.replace(/[-_]/g," ").split(" ").map(t=>t.charAt(0).toUpperCase()+t.slice(1)).join(" ")}createStopButton(){let e=document.createElement("button");return e.className="supernal-tts-stop",e.innerHTML=`
36
+ <svg class="supernal-tts-icon" width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
37
+ <rect x="6" y="6" width="12" height="12"/>
38
+ </svg>
39
+ <span class="supernal-tts-text">Stop</span>
40
+ `,e.setAttribute("aria-label","Stop text-to-speech"),e}createPlayButton(e=!1){let t=document.createElement("button");return t.className="supernal-tts-play",e?(t.classList.add("supernal-tts-play-compact"),t.innerHTML=`
41
+ <svg class="supernal-tts-icon supernal-tts-play-icon" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
42
+ <path d="M8 5v14l11-7z"/>
43
+ </svg>
44
+ <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">
45
+ <circle cx="12" cy="12" r="10" opacity="0.25"></circle>
46
+ <path d="M12 2a10 10 0 0 1 10 10" stroke-linecap="round"></path>
47
+ </svg>
48
+ `):t.innerHTML=`
49
+ <svg class="supernal-tts-icon supernal-tts-play-icon" width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
50
+ <path d="M8 5v14l11-7z"/>
51
+ </svg>
52
+ <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">
53
+ <circle cx="12" cy="12" r="10" opacity="0.25"></circle>
54
+ <path d="M12 2a10 10 0 0 1 10 10" stroke-linecap="round"></path>
55
+ </svg>
56
+ <span class="supernal-tts-text">Listen</span>
57
+ `,t.setAttribute("aria-label","Play text-to-speech"),t}createBrandingBadge(){let e=document.createElement("a");e.className="supernal-badge",e.href="https://www.tts.supernal.ai",e.target="_blank",e.rel="noopener noreferrer",e.title="Powered by Supernal TTS",e.setAttribute("aria-label","Powered by Supernal TTS");let t=document.createElement("img");return t.src="https://unpkg.com/@supernal/tts-widget@latest/dist/supernal-tts-logo.svg",t.alt="Supernal TTS",t.className="supernal-logo-img",t.style.display="block",t.style.width="24px",t.style.height="24px",t.onerror=()=>{e.innerHTML="~+",e.style.fontSize="16px",e.style.fontWeight="700",e.style.display="flex",e.style.color="#0066ff"},e.appendChild(t),e}addDevModeClearButton(){if(document.getElementById("supernal-tts-dev-clear-cache"))return;let e=document.createElement("div");e.id="supernal-tts-dev-clear-cache",e.style.cssText=`
58
+ position: fixed;
59
+ bottom: 20px;
60
+ right: 20px;
61
+ z-index: 100;
62
+ font-family: system-ui, -apple-system, sans-serif;
63
+ `;let t=document.createElement("button");t.className="supernal-tts-dev-button-minimized",t.innerHTML="\u{1F5D1}\uFE0F",t.title="Clear TTS Cache (Dev Mode)",t.style.cssText=`
64
+ display: none;
65
+ background: #6c757d;
66
+ color: white;
67
+ border: none;
68
+ padding: 8px;
69
+ border-radius: 50%;
70
+ width: 36px;
71
+ height: 36px;
72
+ font-size: 16px;
73
+ cursor: pointer;
74
+ box-shadow: 0 2px 8px rgba(0,0,0,0.3);
75
+ transition: all 0.2s ease;
76
+ `;let s=document.createElement("button");s.className="supernal-tts-dev-button",s.style.cssText=`
77
+ background: #dc3545;
78
+ color: white;
79
+ border: none;
80
+ padding: 8px 12px;
81
+ border-radius: 5px;
82
+ font-size: 12px;
83
+ cursor: pointer;
84
+ box-shadow: 0 2px 8px rgba(0,0,0,0.3);
85
+ transition: all 0.2s ease;
86
+ display: flex;
87
+ align-items: center;
88
+ gap: 6px;
89
+ `,s.innerHTML=`
90
+ <span>\u{1F5D1}\uFE0F</span>
91
+ <span>Clear Cache</span>
92
+ <span style="opacity: 0.7; font-size: 10px;">\xD7</span>
93
+ `;let r=i=>{i.addEventListener("mouseenter",()=>{i.style.transform="scale(1.05)",i.style.boxShadow="0 4px 12px rgba(0,0,0,0.4)"}),i.addEventListener("mouseleave",()=>{i.style.transform="scale(1)",i.style.boxShadow="0 2px 8px rgba(0,0,0,0.3)"})};r(s),r(t);let o=()=>{let i=this.cache.size;this.cache.clear(),localStorage.removeItem("supernal-tts-cache"),s.innerHTML=`<span>\u2713</span><span>Cleared ${i}</span><span style="opacity: 0.7; font-size: 10px;">\xD7</span>`,s.style.background="#28a745",setTimeout(()=>{s.innerHTML='<span>\u{1F5D1}\uFE0F</span><span>Clear Cache</span><span style="opacity: 0.7; font-size: 10px;">\xD7</span>',s.style.background="#dc3545"},2e3),console.log(`[Supernal TTS Dev] Cleared ${i} cached items`)};s.addEventListener("click",i=>{i.preventDefault();let a=i.clientX-i.target.getBoundingClientRect().left,l=s.getBoundingClientRect().width;a>l*.8?(s.style.display="none",t.style.display="block",localStorage.setItem("supernal-tts-dev-minimized","true")):o()}),t.addEventListener("click",i=>{i.preventDefault(),t.style.display="none",s.style.display="flex",localStorage.setItem("supernal-tts-dev-minimized","false")}),localStorage.getItem("supernal-tts-dev-minimized")==="true"&&(s.style.display="none",t.style.display="block"),e.appendChild(s),e.appendChild(t),document.body?document.body.appendChild(e):document.addEventListener("DOMContentLoaded",()=>{document.body.appendChild(e)})}async handlePlayClick(e,t,s){let{hash:r,progressBar:o}=s;if(e.classList.contains("supernal-tts-playing")&&this.currentButton===e){this.pauseAudio();return}if(e.classList.contains("supernal-tts-paused")&&this.currentButton===e){this.resumeAudio();return}if(!this.isProcessing){this.currentAudio&&this.currentButton&&this.currentButton!==e&&this.stopAudio();try{this.isProcessing=!0,this.setButtonState(e,"loading");let n=this.getCachedAudioUrl(r);if(!n)if(this.generationMode==="progressive"||this.generationMode==="standard"&&t.length>this.progressiveThreshold){await this.generateAudioProgressive(t,s,e,o);return}else{let a=await this.generateAudio(t,s);n=a.audioUrl.startsWith("http")?a.audioUrl:`${this.apiUrl}${a.audioUrl}`,this.cacheAudioUrl(r,n,a)}await this.playAudio(n,e,o)}catch(n){console.error("TTS Error:",n),this.setButtonState(e,"error"),this.showError(e,n.message),setTimeout(()=>this.setButtonState(e,"ready"),3e3),this.isProcessing=!1}}}async generateAudio(e,t){let s={text:e,options:{provider:t.provider,voice:t.voice,speed:t.speed||1}},r={"Content-Type":"application/json"},o=t.apiKey||this.apiKey;console.log("[TTS Widget] Generate Audio - API Key present:",!!o),console.log("[TTS Widget] Generate Audio - this.apiKey:",!!this.apiKey),console.log("[TTS Widget] Generate Audio - options.apiKey:",!!t.apiKey),o?(r.Authorization=`Bearer ${o}`,console.log("[TTS Widget] Generate Audio - Authorization header added")):console.warn("[TTS Widget] Generate Audio - NO API KEY, Authorization header NOT added");try{let n=await fetch(`${this.apiUrl}/api/v1/generate`,{method:"POST",headers:r,body:JSON.stringify(s)});if(!n.ok){let a=await n.json().catch(()=>({}));throw new Error(a.error||`HTTP ${n.status}: ${n.statusText}`)}let i=await n.json();return{audioUrl:`${this.apiUrl}${i.audioUrl}`,hash:i.hash,cached:i.cached,duration:i.duration,cost:i.cost}}catch(n){if(console.error("TTS generation failed:",n),n.message.includes("Failed to fetch")||n.message.includes("NetworkError")){let i=n.message;throw new Error(`TTS API unreachable: ${i}. Check if ${this.apiUrl} is accessible.`)}throw n}}async generateAudioProgressive(e,t,s,r){let o={text:e,options:{provider:t.provider,voice:t.voice,speed:t.speed||1}},n={"Content-Type":"application/json",Accept:"text/event-stream"},i=t.apiKey||this.apiKey;i&&(n.Authorization=`Bearer ${i}`);try{let a=await fetch(`${this.apiUrl}/api/v1/generate-progressive`,{method:"POST",headers:n,body:JSON.stringify(o)});if(!a.ok){let c=await a.json().catch(()=>({}));throw new Error(c.error||`HTTP ${a.status}: ${a.statusText}`)}if(!a.body)throw new Error("No response body for SSE stream");let l=a.body.getReader(),d=new TextDecoder,h="",g=0,v=0,f=(c,m)=>{if(r){let T=c/m*100,y=r.querySelector(".supernal-tts-progress-slider");y&&(y.value=T.toString());let u=r.querySelector(".current");u&&(u.textContent=`Chunk ${c}/${m}`)}};for(;;){let{done:c,value:m}=await l.read();if(c)break;h+=d.decode(m,{stream:!0});let T=h.split(`
94
+
95
+ `);h=T.pop()||"";for(let y of T)if(y.startsWith("data: "))try{let u=JSON.parse(y.substring(6));switch(u.type){case"started":g=u.totalChunks,console.log(`[Progressive TTS] Started: ${g} chunks`),this.setButtonState(s,"loading");break;case"progress":v=u.completed,console.log(`[Progressive TTS] Progress: ${v}/${g}`),f(v,g);break;case"complete":console.log("[Progressive TTS] Complete:",u);let p=u.audioUrl.startsWith("http")?u.audioUrl:`${this.apiUrl}${u.audioUrl}`;this.cacheAudioUrl(t.hash,p,{hash:u.hash,cached:!1,duration:u.duration,cost:u.cost}),await this.playAudio(p,s,r);break;case"error":throw new Error(u.message||"Progressive generation failed")}}catch(u){console.warn("[Progressive TTS] Failed to parse event:",y,u)}}}catch(a){throw console.error("Progressive TTS generation failed:",a),this.isProcessing=!1,a}}async playAudio(e,t,s,r){return new Promise((o,n)=>{let i=new Audio(e);this.currentAudio=i,this.currentButton=t,this.clientSideSpeed&&(i.preservesPitch=!0,i.mozPreservesPitch=!0,i.webkitPreservesPitch=!0),r!==void 0&&this.clientSideSpeed&&(i.playbackRate=r),this.progressUpdateInterval&&(clearInterval(this.progressUpdateInterval),this.progressUpdateInterval=null),i.addEventListener("loadstart",()=>{this.currentAudio===i&&this.setButtonState(t,"loading")}),i.addEventListener("canplay",()=>{if(this.currentAudio===i){this.setButtonState(t,"playing"),this.isProcessing=!1;let a=i.play();a!==void 0&&a.catch(l=>{console.error("Audio play failed:",l),this.currentAudio===i&&(this.setButtonState(t,"error"),this.currentAudio=null,this.currentButton=null,this.isProcessing=!1,n(new Error("Audio playback failed")))}),s&&(s.classList.add("playing"),this.progressUpdateInterval=window.setInterval(()=>{this.updateProgressBar(i,s)},100))}}),i.addEventListener("ended",()=>{if(this.currentAudio===i){if(this.setButtonState(t,"ready"),this.currentAudio=null,this.currentButton=null,this.progressUpdateInterval&&(clearInterval(this.progressUpdateInterval),this.progressUpdateInterval=null),s){s.classList.remove("playing");let a=s.querySelector(".supernal-tts-progress-slider");a&&(a.value="0");let l=s.querySelector(".supernal-tts-time-display");l&&(l.innerHTML='<span class="current">0:00</span> / <span class="duration">0:00</span>')}o()}}),i.addEventListener("error",a=>{this.currentAudio===i&&(this.setButtonState(t,"error"),this.currentAudio=null,this.currentButton=null,this.progressUpdateInterval&&(clearInterval(this.progressUpdateInterval),this.progressUpdateInterval=null),s&&s.classList.remove("playing"),n(new Error("Audio playback failed")))}),i.load()})}updateProgressBar(e,t){if(!e||!t)return;let s=t.querySelector(".supernal-tts-progress-slider"),r=t.querySelector(".current"),o=t.querySelector(".duration");if(s&&!isNaN(e.duration)){let n=e.currentTime/e.duration*100;s.value=n.toString()}r&&(r.textContent=this.formatTime(e.currentTime)),o&&!isNaN(e.duration)&&(o.textContent=this.formatTime(e.duration))}pauseAudio(){this.currentAudio&&!this.currentAudio.paused&&this.currentAudio.pause(),this.currentButton&&this.setButtonState(this.currentButton,"paused")}resumeAudio(){if(this.currentAudio&&this.currentAudio.paused){let e=this.currentAudio.play();e!==void 0&&e.then(()=>{this.currentButton&&this.setButtonState(this.currentButton,"playing")}).catch(t=>{console.error("Audio resume failed:",t),this.currentButton&&this.setButtonState(this.currentButton,"error")})}}stopAudio(){this.progressUpdateInterval&&(clearInterval(this.progressUpdateInterval),this.progressUpdateInterval=null),this.currentAudio&&(this.currentAudio.pause(),this.currentAudio.currentTime=0,this.currentAudio=null),this.currentButton&&(this.setButtonState(this.currentButton,"ready"),this.currentButton=null),this.isProcessing=!1}cleanStopAudio(e){if(this.progressUpdateInterval&&(clearInterval(this.progressUpdateInterval),this.progressUpdateInterval=null),this.currentAudio&&(this.currentAudio.pause(),this.currentAudio.currentTime=0,this.currentAudio=null),this.currentButton&&(this.setButtonState(this.currentButton,"ready"),this.currentButton=null),e){e.classList.remove("playing");let t=e.querySelector(".supernal-tts-progress-slider");t&&(t.value="0");let s=e.querySelector(".supernal-tts-time-display");s&&(s.innerHTML='<span class="current">0:00</span> / <span class="duration">0:00</span>')}this.isProcessing=!1}cleanStopAudioKeepProgress(e){this.progressUpdateInterval&&(clearInterval(this.progressUpdateInterval),this.progressUpdateInterval=null),this.currentAudio&&(this.currentAudio.pause(),this.currentAudio.currentTime=0,this.currentAudio=null),this.currentButton&&this.setButtonState(this.currentButton,"loading"),this.isProcessing=!1}async handlePlayClickFromPosition(e,t,s,r){let{hash:o,progressBar:n}=s;if(!this.isProcessing)try{this.isProcessing=!0,this.setButtonState(e,"loading");let i=this.getCachedAudioUrl(o);if(!i){let a=await this.generateAudio(t,s);i=a.audioUrl.startsWith("http")?a.audioUrl:`${this.apiUrl}${a.audioUrl}`,this.cacheAudioUrl(o,i,a)}await this.playAudioFromPosition(i,e,n,r)}catch(i){console.error("TTS Error:",i),this.setButtonState(e,"error"),this.showError(e,i.message),setTimeout(()=>this.setButtonState(e,"ready"),3e3),this.isProcessing=!1}}async playAudioFromPosition(e,t,s,r){return new Promise((o,n)=>{let i=new Audio(e);this.currentAudio=i,this.currentButton=t,this.clientSideSpeed&&(i.preservesPitch=!0,i.mozPreservesPitch=!0,i.webkitPreservesPitch=!0),this.progressUpdateInterval&&(clearInterval(this.progressUpdateInterval),this.progressUpdateInterval=null);let a=!1;i.addEventListener("loadstart",()=>{this.currentAudio===i&&this.setButtonState(t,"loading")}),i.addEventListener("loadedmetadata",()=>{this.currentAudio===i&&!a&&r>0&&(i.currentTime=i.duration*r,a=!0)}),i.addEventListener("canplay",()=>{if(this.currentAudio===i){!a&&r>0&&i.duration>0&&(i.currentTime=i.duration*r,a=!0),this.setButtonState(t,"playing"),this.isProcessing=!1;let l=i.play();l!==void 0&&l.catch(d=>{console.error("Audio play failed:",d),this.currentAudio===i&&(this.setButtonState(t,"error"),this.currentAudio=null,this.currentButton=null,this.isProcessing=!1,n(new Error("Audio playback failed")))}),s&&(s.classList.add("playing"),this.progressUpdateInterval=window.setInterval(()=>{this.updateProgressBar(i,s)},100))}}),i.addEventListener("ended",()=>{if(this.currentAudio===i){if(this.setButtonState(t,"ready"),this.currentAudio=null,this.currentButton=null,this.progressUpdateInterval&&(clearInterval(this.progressUpdateInterval),this.progressUpdateInterval=null),s){s.classList.remove("playing");let l=s.querySelector(".supernal-tts-progress-slider");l&&(l.value="0");let d=s.querySelector(".supernal-tts-time-display");d&&(d.innerHTML='<span class="current">0:00</span> / <span class="duration">0:00</span>')}o()}}),i.addEventListener("error",l=>{this.currentAudio===i&&(this.setButtonState(t,"error"),this.currentAudio=null,this.currentButton=null,this.progressUpdateInterval&&(clearInterval(this.progressUpdateInterval),this.progressUpdateInterval=null),s&&s.classList.remove("playing"),n(new Error("Audio playback failed")))}),i.load()})}setButtonState(e,t){e.classList.remove("supernal-tts-loading","supernal-tts-playing","supernal-tts-paused","supernal-tts-error");let s=e.querySelector(".supernal-tts-play-icon"),r=e.querySelector(".supernal-tts-text");switch(t){case"loading":e.classList.add("supernal-tts-loading"),e.disabled=!0,r&&(r.textContent="Loading...");break;case"playing":e.classList.add("supernal-tts-playing"),e.disabled=!1,s&&(s.innerHTML='<rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/>'),r&&(r.textContent="Pause");break;case"paused":e.classList.add("supernal-tts-paused"),e.disabled=!1,s&&(s.innerHTML='<path d="M8 5v14l11-7z"/>'),r&&(r.textContent="Resume");break;case"error":e.classList.add("supernal-tts-error"),e.disabled=!1,s&&(s.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"/>'),r&&(r.textContent="Error");break;default:e.disabled=!1,s&&(s.innerHTML='<path d="M8 5v14l11-7z"/>'),r&&(r.textContent="Listen");break}}showError(e,t){let s=document.createElement("div");s.className="supernal-tts-error-tooltip",t.includes("development mode")?s.textContent=t:t.includes("Failed to fetch")?s.textContent="Service unavailable (dev mode)":s.textContent="Audio generation failed",e.parentNode?.appendChild(s),setTimeout(()=>{s.parentNode&&s.parentNode.removeChild(s)},3e3)}generateHash(e,t={}){let s=e+JSON.stringify(t),r=0;for(let o=0;o<s.length;o++){let n=s.charCodeAt(o);r=(r<<5)-r+n,r=r&r}return Math.abs(r).toString(36)}getCachedAudioUrl(e){let t=this.cache.get(e);if(!t)return null;if(Date.now()>t.expiry)return this.cache.delete(e),this.saveCacheToStorage(),null;let s=t.audioUrl;return s.startsWith("http")?s:`${this.apiUrl}${s}`}cacheAudioUrl(e,t,s={}){this.cache.set(e,{audioUrl:t,metadata:s,expiry:Date.now()+this.cacheExpiry,timestamp:Date.now()}),this.saveCacheToStorage()}loadCacheFromStorage(){try{let e=localStorage.getItem("supernal-tts-cache");if(e){let t=JSON.parse(e);this.cache=new Map(Object.entries(t))}}catch(e){console.warn("Failed to load TTS cache:",e)}}saveCacheToStorage(){try{let e=Object.fromEntries(this.cache);localStorage.setItem("supernal-tts-cache",JSON.stringify(e))}catch(e){console.warn("Failed to save TTS cache:",e)}}static init(e){return window.SupernalTTSInstance?(console.debug("SupernalTTS already initialized, returning existing instance"),window.SupernalTTSInstance):(window.SupernalTTSInstance=new w(e),window.SupernalTTSInstance)}static getInstance(){return window.SupernalTTSInstance}addWidget(e,t,s={}){let r=null;if(typeof e=="string"?r=document.querySelector(e):r=e,!r){console.error("TTS Widget: Element not found");return}r.classList.add("supernal-tts-widget"),r.dataset.text=t,s.voice&&(r.dataset.voice=s.voice),s.provider&&(r.dataset.provider=s.provider),this.setupWidget(r)}};if(typeof window<"u")if(window.SupernalTTS)console.debug("SupernalTTS already loaded, skipping redeclaration");else{window.SupernalTTS=L;let w=document.querySelector("script[data-supernal-tts-auto-init]");if(w&&w.dataset){let e=JSON.parse(w.dataset.supernalTtsAutoInit||"{}");L.init(e)}}var b=L;export{L as SupernalTTS,b as default};
96
+ //# sourceMappingURL=widget.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/widget.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"],
5
+ "mappings": "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",
6
+ "names": ["SupernalTTS", "_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", "widget_default"]
7
+ }