@supernal/tts-widget 1.0.1 → 1.0.5

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.
package/README.md CHANGED
@@ -178,13 +178,51 @@ SupernalTTS.init({
178
178
  provider: 'openai', // Optional: TTS provider (default: 'openai')
179
179
  voice: 'alloy', // Optional: Voice selection
180
180
  speed: 1.0, // Optional: Playback speed (0.25-4.0)
181
- apiKey: 'your-api-key', // Optional: For authenticated requests
181
+ apiKey: 'your-api-key', // Optional: For authenticated requests (auto-detected from env)
182
182
  devMode: false, // Optional: Enable dev mode features
183
183
  clientSideSpeed: true // Optional: Use browser pitch-preserving time-stretching (default: true)
184
184
  // Saves generation costs! Set to false for server-side regeneration
185
185
  });
186
186
  ```
187
187
 
188
+ ### Automatic API Key Detection
189
+
190
+ The widget automatically detects API keys from common framework environment variables, eliminating boilerplate:
191
+
192
+ ```javascript
193
+ // ✅ Minimal configuration - API key auto-detected
194
+ SupernalTTS.init({
195
+ apiUrl: window.location.origin
196
+ // provider defaults to 'openai'
197
+ // apiKey auto-detected from NEXT_PUBLIC_TTS_API_KEY (or similar)
198
+ });
199
+ ```
200
+
201
+ **Supported environment variables (checked in order):**
202
+ - `NEXT_PUBLIC_TTS_API_KEY` - Next.js
203
+ - `VITE_TTS_API_KEY` - Vite
204
+ - `PUBLIC_TTS_API_KEY` - SvelteKit, Astro
205
+ - `REACT_APP_TTS_API_KEY` - Create React App
206
+
207
+ **How it works:**
208
+ 1. Widget checks for these variables at runtime (bundlers expose them client-side)
209
+ 2. If found, automatically uses the key for API authentication
210
+ 3. You can still override by explicitly passing `apiKey` option
211
+ 4. Only works in browser (server-side rendering won't auto-detect)
212
+
213
+ **Example - Next.js:**
214
+ ```bash
215
+ # .env.local
216
+ NEXT_PUBLIC_TTS_API_KEY=your-api-key-here
217
+ ```
218
+
219
+ ```javascript
220
+ // No need to pass apiKey - auto-detected!
221
+ SupernalTTS.init({
222
+ apiUrl: 'https://tts.supernal.ai'
223
+ });
224
+ ```
225
+
188
226
  ### Client-Side Speed Adjustment
189
227
 
190
228
  By default (`clientSideSpeed: true`), the widget uses the browser's native `preservesPitch` feature with `playbackRate` to adjust speed instantly without regenerating audio. This:
package/dist/loader.js CHANGED
@@ -1,38 +1,38 @@
1
- var M=Object.defineProperty;var x=(S,e)=>()=>(S&&(e=S(S=0)),e);var P=(S,e)=>{for(var t in e)M(S,t,{get:e[t],enumerable:!0})};var A={};P(A,{SupernalTTS:()=>L,default:()=>B});var L,B,C=x(()=>{"use strict";L=class S{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(i=>{if(i.nodeType===Node.ELEMENT_NODE){let o=i;o.classList?.contains("supernal-tts-widget")?this.setupWidget(o):o.querySelectorAll?.(".supernal-tts-widget")?.forEach(r=>this.setupWidget(r))}})})}).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}),i=e.dataset.controls;if(i==="advanced"||i==="true"){this.setupAdvancedWidget(e,t.text,{voice:t.voice,provider:t.provider,speed:t.speed,hash:s,apiKey:t.apiKey});return}else if(i==="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,i=e.dataset.provider||this.defaultProvider,o=e.dataset.speed?parseFloat(e.dataset.speed):this.defaultSpeed,n=e.dataset.voices||"",r=n?n.split(",").map(p=>p.trim()).filter(Boolean):[],a=e.dataset.enableSpeed==="true",l=e.dataset.enableProgress==="true",d=e.dataset.apiKey||this.apiKey;return{text:t,voices:r,enableSpeed:a,enableProgress:l,speed:o,provider:i,voice:s,apiKey:d}}setupBasicWidget(e,t,s){let{voice:i,provider:o,speed:n,hash:r,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 p=this.createBrandingBadge();l.appendChild(p)}e.appendChild(l),e.classList.add("supernal-tts-widget-initialized"),d.addEventListener("click",p=>{p.preventDefault(),this.handlePlayClick(d,t,{voice:i,provider:o,speed:n,hash:r,apiKey:a})}),e._ttsWidget={text:t,voice:i,provider:o,speed:n,hash:r,playButton:d}}setupCompactWidget(e,t,s){let{hash:i,apiKey:o}=s,{voice:n,provider:r,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 p=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(p),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=`
1
+ var M=Object.defineProperty;var x=(S,e)=>()=>(S&&(e=S(S=0)),e);var P=(S,e)=>{for(var t in e)M(S,t,{get:e[t],enumerable:!0})};var A={};P(A,{SupernalTTS:()=>L,default:()=>B});var L,B,C=x(()=>{"use strict";L=class S{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||this.detectApiKey(),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()}detectApiKey(){if(typeof window>"u")return;let e=["NEXT_PUBLIC_TTS_API_KEY","VITE_TTS_API_KEY","PUBLIC_TTS_API_KEY","REACT_APP_TTS_API_KEY"];for(let t of e)try{let s=globalThis.process?.env;if(s?.[t])return console.log(`[TTS Widget] Auto-detected API key from process.env.${t}`),s[t];let n=globalThis.import?.meta?.env;if(n?.[t])return console.log(`[TTS Widget] Auto-detected API key from import.meta.env.${t}`),n[t]}catch{continue}}init(){document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>this.initializeWidgets()):this.initializeWidgets(),this.observeDOM()}observeDOM(){new MutationObserver(t=>{t.forEach(s=>{s.addedNodes.forEach(n=>{if(n.nodeType===Node.ELEMENT_NODE){let o=n;o.classList?.contains("supernal-tts-widget")?this.setupWidget(o):o.querySelectorAll?.(".supernal-tts-widget")?.forEach(r=>this.setupWidget(r))}})})}).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}),n=e.dataset.controls;if(n==="advanced"||n==="true"){this.setupAdvancedWidget(e,t.text,{voice:t.voice,provider:t.provider,speed:t.speed,hash:s,apiKey:t.apiKey});return}else if(n==="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)}scrubText(e){if(!e)return"";let t=e;return t=t.replace(/\[([^\]]+)\]\(([^)]+)\)/g,"$1"),t=t.replace(/https?:\/\/[^\s]+/g,"link"),t=t.replace(/www\.[^\s]+/g,"link"),t}parseWidgetConfig(e){let t=e.dataset.text||e.textContent?.trim()||"",s=this.scrubText(t),n=e.dataset.voice||this.defaultVoice,o=e.dataset.provider||this.defaultProvider,i=e.dataset.speed?parseFloat(e.dataset.speed):this.defaultSpeed,r=e.dataset.voices||"",a=r?r.split(",").map(h=>h.trim()).filter(Boolean):[],l=e.dataset.enableSpeed==="true",d=e.dataset.enableProgress==="true",p=e.dataset.apiKey||this.apiKey;return{text:s,voices:a,enableSpeed:l,enableProgress:d,speed:i,provider:o,voice:n,apiKey:p}}setupBasicWidget(e,t,s){let{voice:n,provider:o,speed:i,hash:r,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 p=this.createBrandingBadge();l.appendChild(p)}e.appendChild(l),e.classList.add("supernal-tts-widget-initialized"),d.addEventListener("click",p=>{p.preventDefault(),this.handlePlayClick(d,t,{voice:n,provider:o,speed:i,hash:r,apiKey:a})}),e._ttsWidget={text:t,voice:n,provider:o,speed:i,hash:r,playButton:d}}setupCompactWidget(e,t,s){let{hash:n,apiKey:o}=s,{voice:i,provider:r,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 p=this.createPlayButton(),h=document.createElement("select");if(h.className="supernal-tts-voice-select",h.innerHTML=`
2
+ <option value="alloy" ${i==="alloy"?"selected":""}>Alloy</option>
3
+ <option value="echo" ${i==="echo"?"selected":""}>Echo</option>
4
+ <option value="fable" ${i==="fable"?"selected":""}>Fable</option>
5
+ <option value="onyx" ${i==="onyx"?"selected":""}>Onyx</option>
6
+ <option value="nova" ${i==="nova"?"selected":""}>Nova</option>
7
+ <option value="shimmer" ${i==="shimmer"?"selected":""}>Shimmer</option>
8
+ <option value="coral" ${i==="coral"?"selected":""}>Coral</option>
9
+ `,d.appendChild(p),d.appendChild(h),this.showBranding){let m=this.createBrandingBadge();d.appendChild(m)}let v=document.createElement("div");v.className="supernal-tts-speed-control",v.innerHTML=`
10
10
  <label>Speed: <span class="supernal-tts-speed-value">${a}x</span></label>
11
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");p.addEventListener("click",m=>{m.preventDefault(),a=parseFloat(f.value),n=g.value;let T=this.generateHash(t,{voice:n,provider:r,speed:a});this.handlePlayClick(p,t,{voice:n,provider:r,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:r,speed:a,hash:i,playButton:p,compact:!0}}setupAdvancedWidget(e,t,s){let{hash:i,apiKey:o}=s,{voice:n,provider:r,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 p=this.createPlayButton(),g=this.createStopButton();d.appendChild(p),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>
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");p.addEventListener("click",m=>{m.preventDefault(),a=parseFloat(f.value),i=h.value;let T=this.generateHash(t,{voice:i,provider:r,speed:a});this.handlePlayClick(p,t,{voice:i,provider:r,speed:a,hash:T,apiKey:o})}),f.addEventListener("input",m=>{a=parseFloat(m.target.value),c.textContent=`${a}x`}),h.addEventListener("change",()=>{i=h.value}),e._ttsWidget={text:t,voice:i,provider:r,speed:a,hash:n,playButton:p,compact:!0}}setupAdvancedWidget(e,t,s){let{hash:n,apiKey:o}=s,{voice:i,provider:r,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 p=this.createPlayButton(),h=this.createStopButton();d.appendChild(p),d.appendChild(h);let v=document.createElement("select");v.className="supernal-tts-voice-select",v.innerHTML=`
13
+ <option value="alloy" ${i==="alloy"?"selected":""}>Alloy</option>
14
+ <option value="echo" ${i==="echo"?"selected":""}>Echo</option>
15
+ <option value="fable" ${i==="fable"?"selected":""}>Fable</option>
16
+ <option value="onyx" ${i==="onyx"?"selected":""}>Onyx</option>
17
+ <option value="nova" ${i==="nova"?"selected":""}>Nova</option>
18
+ <option value="shimmer" ${i==="shimmer"?"selected":""}>Shimmer</option>
19
+ <option value="coral" ${i==="coral"?"selected":""}>Coral</option>
20
20
  `;let f=document.createElement("div");f.className="supernal-tts-speed-control",f.innerHTML=`
21
21
  <label>Speed: <span class="supernal-tts-speed-value">${a}x</span></label>
22
22
  <input type="range" class="supernal-tts-speed-slider" min="0.25" max="4.0" step="0.25" value="${a}">
23
23
  `;let c=document.createElement("div");if(c.className="supernal-tts-progress",c.innerHTML=`
24
24
  <input type="range" class="supernal-tts-progress-slider" min="0" max="100" value="0" step="0.1">
25
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 h=this.createBrandingBadge();l.appendChild(h)}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");p.addEventListener("click",h=>{h.preventDefault(),a=parseFloat(m.value),n=v.value;let E=this.generateHash(t,{voice:n,provider:r,speed:a});this.handlePlayClick(p,t,{voice:n,provider:r,speed:a,hash:E,apiKey:o})}),g.addEventListener("click",h=>{h.preventDefault(),this.stopAudio()}),m.addEventListener("input",h=>{a=parseFloat(h.target.value),T.textContent=`${a}x`}),v.addEventListener("change",()=>{n=v.value}),y.addEventListener("input",h=>{if(this.currentAudio){let E=parseFloat(h.target.value)/100;this.currentAudio.currentTime=this.currentAudio.duration*E}}),setInterval(()=>{if(this.currentAudio&&e._ttsWidget===this.currentWidget){let h=this.currentAudio.currentTime/this.currentAudio.duration*100;y.value=h.toString();let E=this.formatTime(this.currentAudio.currentTime),w=this.formatTime(this.currentAudio.duration);c.querySelector(".supernal-tts-current-time").textContent=E,c.querySelector(".supernal-tts-duration").textContent=w}},100),e._ttsWidget={text:t,voice:n,provider:r,speed:a,hash:i,playButton:p,stopButton:g,voiceSelect:v,speedSlider:m,progressSlider:y,advanced:!0},this.currentWidget=e._ttsWidget}setupModularWidget(e,t,s){let i=t.voice,o=t.speed,{text:n,voices:r,enableSpeed:a,enableProgress:l,provider:d,apiKey:p}=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;r.length>0&&(m=this.createVoiceDropdown(r,i),f.appendChild(m),m.addEventListener("voice-changed",u=>{let h=u.detail.voice;if(h!==i&&this.currentButton===c&&this.currentAudio){i=h,this.currentAudio.duration>0&&(g=this.currentAudio.currentTime/this.currentAudio.duration);let w=e._ttsWidget?.progressBar;this.cleanStopAudioKeepProgress(w),setTimeout(()=>{this.handlePlayClickFromPosition(c,n,{voice:i,provider:d,speed:o,hash:this.generateHash(n,{voice:i,provider:d,speed:o}),progressBar:w,apiKey:p},g)},50)}else i=h}));let T=null;if(a&&(T=this.createSpeedDropdown(o),f.appendChild(T),T.addEventListener("speed-changed",u=>{let h=u.detail.speed;if(h!==o){if(o=h,this.clientSideSpeed&&this.currentAudio&&this.currentButton===c)this.currentAudio.playbackRate=h;else if(!this.clientSideSpeed&&this.currentButton===c&&this.currentAudio){this.currentAudio.duration>0&&(g=this.currentAudio.currentTime/this.currentAudio.duration);let w=e._ttsWidget?.progressBar;this.cleanStopAudioKeepProgress(w),setTimeout(()=>{this.handlePlayClickFromPosition(c,n,{voice:i,provider:d,speed:o,hash:this.generateHash(n,{voice:i,provider:d,speed:o}),progressBar:w,apiKey:p},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 h=this.generateHash(n,{voice:i,provider:d,speed:o});this.handlePlayClick(c,n,{voice:i,provider:d,speed:o,hash:h,progressBar:y,apiKey:p})}),e._ttsWidget={text:n,voice:i,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 i=document.createElement("button");i.className="supernal-tts-voice-toggle",i.setAttribute("aria-label","Select voice"),i.setAttribute("aria-expanded","false"),i.innerHTML=`
26
+ `,l.appendChild(d),l.appendChild(v),l.appendChild(f),l.appendChild(c),this.showBranding){let g=this.createBrandingBadge();l.appendChild(g)}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");p.addEventListener("click",g=>{g.preventDefault(),a=parseFloat(m.value),i=v.value;let E=this.generateHash(t,{voice:i,provider:r,speed:a});this.handlePlayClick(p,t,{voice:i,provider:r,speed:a,hash:E,apiKey:o})}),h.addEventListener("click",g=>{g.preventDefault(),this.stopAudio()}),m.addEventListener("input",g=>{a=parseFloat(g.target.value),T.textContent=`${a}x`}),v.addEventListener("change",()=>{i=v.value}),y.addEventListener("input",g=>{if(this.currentAudio){let E=parseFloat(g.target.value)/100;this.currentAudio.currentTime=this.currentAudio.duration*E}}),setInterval(()=>{if(this.currentAudio&&e._ttsWidget===this.currentWidget){let g=this.currentAudio.currentTime/this.currentAudio.duration*100;y.value=g.toString();let E=this.formatTime(this.currentAudio.currentTime),w=this.formatTime(this.currentAudio.duration);c.querySelector(".supernal-tts-current-time").textContent=E,c.querySelector(".supernal-tts-duration").textContent=w}},100),e._ttsWidget={text:t,voice:i,provider:r,speed:a,hash:n,playButton:p,stopButton:h,voiceSelect:v,speedSlider:m,progressSlider:y,advanced:!0},this.currentWidget=e._ttsWidget}setupModularWidget(e,t,s){let n=t.voice,o=t.speed,{text:i,voices:r,enableSpeed:a,enableProgress:l,provider:d,apiKey:p}=t,h=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;r.length>0&&(m=this.createVoiceDropdown(r,n),f.appendChild(m),m.addEventListener("voice-changed",u=>{let g=u.detail.voice;if(g!==n&&this.currentButton===c&&this.currentAudio){n=g,this.currentAudio.duration>0&&(h=this.currentAudio.currentTime/this.currentAudio.duration);let w=e._ttsWidget?.progressBar;this.cleanStopAudioKeepProgress(w),setTimeout(()=>{this.handlePlayClickFromPosition(c,i,{voice:n,provider:d,speed:o,hash:this.generateHash(i,{voice:n,provider:d,speed:o}),progressBar:w,apiKey:p},h)},50)}else n=g}));let T=null;if(a&&(T=this.createSpeedDropdown(o),f.appendChild(T),T.addEventListener("speed-changed",u=>{let g=u.detail.speed;if(g!==o){if(o=g,this.clientSideSpeed&&this.currentAudio&&this.currentButton===c)this.currentAudio.playbackRate=g;else if(!this.clientSideSpeed&&this.currentButton===c&&this.currentAudio){this.currentAudio.duration>0&&(h=this.currentAudio.currentTime/this.currentAudio.duration);let w=e._ttsWidget?.progressBar;this.cleanStopAudioKeepProgress(w),setTimeout(()=>{this.handlePlayClickFromPosition(c,i,{voice:n,provider:d,speed:o,hash:this.generateHash(i,{voice:n,provider:d,speed:o}),progressBar:w,apiKey:p},h)},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 g=this.generateHash(i,{voice:n,provider:d,speed:o});this.handlePlayClick(c,i,{voice:n,provider:d,speed:o,hash:g,progressBar:y,apiKey:p})}),e._ttsWidget={text:i,voice:n,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 n=document.createElement("button");n.className="supernal-tts-voice-toggle",n.setAttribute("aria-label","Select voice"),n.setAttribute("aria-expanded","false"),n.innerHTML=`
27
27
  <svg class="supernal-tts-voice-icon" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
28
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
29
  </svg>
30
- `;let o=document.createElement("div");o.className="supernal-tts-voice-dropdown hidden",o.setAttribute("role","menu"),e.forEach(r=>{let a=document.createElement("button");a.className="supernal-tts-voice-option",a.textContent=this.formatVoiceName(r),a.dataset.voice=r,a.setAttribute("role","menuitem"),r===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:r}}))}),o.appendChild(a)});let n=null;return s.addEventListener("mouseenter",()=>{n&&(clearTimeout(n),n=null),o.classList.remove("hidden"),i.setAttribute("aria-expanded","true"),setTimeout(()=>{let r=o.getBoundingClientRect(),a=window.innerHeight;r.bottom>a&&r.top>r.height?(o.style.bottom="100%",o.style.top="auto"):(o.style.top="calc(100% + 4px)",o.style.bottom="auto")},0)}),s.addEventListener("mouseleave",r=>{n=window.setTimeout(()=>{o.classList.add("hidden"),i.setAttribute("aria-expanded","false")},150)}),o.addEventListener("mouseenter",()=>{n&&(clearTimeout(n),n=null)}),o.addEventListener("mouseleave",()=>{n=window.setTimeout(()=>{o.classList.add("hidden"),i.setAttribute("aria-expanded","false")},150)}),i.addEventListener("click",r=>{r.stopPropagation();let a=o.classList.contains("hidden");o.classList.toggle("hidden"),i.setAttribute("aria-expanded",a?"true":"false")}),s.appendChild(i),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=`
30
+ `;let o=document.createElement("div");o.className="supernal-tts-voice-dropdown hidden",o.setAttribute("role","menu"),e.forEach(r=>{let a=document.createElement("button");a.className="supernal-tts-voice-option",a.textContent=this.formatVoiceName(r),a.dataset.voice=r,a.setAttribute("role","menuitem"),r===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:r}}))}),o.appendChild(a)});let i=null;return s.addEventListener("mouseenter",()=>{i&&(clearTimeout(i),i=null),o.classList.remove("hidden"),n.setAttribute("aria-expanded","true"),setTimeout(()=>{let r=o.getBoundingClientRect(),a=window.innerHeight;r.bottom>a&&r.top>r.height?(o.style.bottom="100%",o.style.top="auto"):(o.style.top="calc(100% + 4px)",o.style.bottom="auto")},0)}),s.addEventListener("mouseleave",r=>{i=window.setTimeout(()=>{o.classList.add("hidden"),n.setAttribute("aria-expanded","false")},150)}),o.addEventListener("mouseenter",()=>{i&&(clearTimeout(i),i=null)}),o.addEventListener("mouseleave",()=>{i=window.setTimeout(()=>{o.classList.add("hidden"),n.setAttribute("aria-expanded","false")},150)}),n.addEventListener("click",r=>{r.stopPropagation();let a=o.classList.contains("hidden");o.classList.toggle("hidden"),n.setAttribute("aria-expanded",a?"true":"false")}),s.appendChild(n),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
31
  <svg class="supernal-tts-speed-icon" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
32
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
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
34
  </svg>
35
- `;let i=document.createElement("div");i.className="supernal-tts-speed-dropdown hidden",i.setAttribute("role","menu");let o=[];for(let r=.6;r<=3;r+=.2)o.push(Math.round(r*10)/10);o.forEach(r=>{let a=document.createElement("button");a.className="supernal-tts-speed-option",a.textContent=`${r.toFixed(1)}x`,a.dataset.speed=r.toString(),a.setAttribute("role","menuitem"),Math.abs(r-e)<.01&&a.classList.add("active"),a.addEventListener("click",l=>{l.stopPropagation(),l.preventDefault(),i.querySelectorAll(".supernal-tts-speed-option").forEach(d=>{d.classList.remove("active")}),a.classList.add("active"),t.dispatchEvent(new CustomEvent("speed-changed",{detail:{speed:r}}))}),i.appendChild(a)});let n=null;return t.addEventListener("mouseenter",()=>{n&&(clearTimeout(n),n=null),i.classList.remove("hidden"),setTimeout(()=>{let r=i.getBoundingClientRect(),a=window.innerHeight;r.bottom>a&&r.top>r.height?(i.style.bottom="100%",i.style.top="auto"):(i.style.top="calc(100% + 4px)",i.style.bottom="auto")},0)}),t.addEventListener("mouseleave",()=>{n=window.setTimeout(()=>{i.classList.add("hidden")},150)}),i.addEventListener("mouseenter",()=>{n&&(clearTimeout(n),n=null)}),i.addEventListener("mouseleave",()=>{n=window.setTimeout(()=>{i.classList.add("hidden")},150)}),t.appendChild(s),t.appendChild(i),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",i=>{if(this.currentAudio){let o=parseFloat(i.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=`
35
+ `;let n=document.createElement("div");n.className="supernal-tts-speed-dropdown hidden",n.setAttribute("role","menu");let o=[];for(let r=.6;r<=3;r+=.2)o.push(Math.round(r*10)/10);o.forEach(r=>{let a=document.createElement("button");a.className="supernal-tts-speed-option",a.textContent=`${r.toFixed(1)}x`,a.dataset.speed=r.toString(),a.setAttribute("role","menuitem"),Math.abs(r-e)<.01&&a.classList.add("active"),a.addEventListener("click",l=>{l.stopPropagation(),l.preventDefault(),n.querySelectorAll(".supernal-tts-speed-option").forEach(d=>{d.classList.remove("active")}),a.classList.add("active"),t.dispatchEvent(new CustomEvent("speed-changed",{detail:{speed:r}}))}),n.appendChild(a)});let i=null;return t.addEventListener("mouseenter",()=>{i&&(clearTimeout(i),i=null),n.classList.remove("hidden"),setTimeout(()=>{let r=n.getBoundingClientRect(),a=window.innerHeight;r.bottom>a&&r.top>r.height?(n.style.bottom="100%",n.style.top="auto"):(n.style.top="calc(100% + 4px)",n.style.bottom="auto")},0)}),t.addEventListener("mouseleave",()=>{i=window.setTimeout(()=>{n.classList.add("hidden")},150)}),n.addEventListener("mouseenter",()=>{i&&(clearTimeout(i),i=null)}),n.addEventListener("mouseleave",()=>{i=window.setTimeout(()=>{n.classList.add("hidden")},150)}),t.appendChild(s),t.appendChild(n),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",n=>{if(this.currentAudio){let o=parseFloat(n.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
36
  <svg class="supernal-tts-icon" width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
37
37
  <rect x="6" y="6" width="12" height="12"/>
38
38
  </svg>
@@ -90,7 +90,7 @@ var M=Object.defineProperty;var x=(S,e)=>()=>(S&&(e=S(S=0)),e);var P=(S,e)=>{for
90
90
  <span>\u{1F5D1}\uFE0F</span>
91
91
  <span>Clear Cache</span>
92
92
  <span style="opacity: 0.7; font-size: 10px;">\xD7</span>
93
- `;let i=r=>{r.addEventListener("mouseenter",()=>{r.style.transform="scale(1.05)",r.style.boxShadow="0 4px 12px rgba(0,0,0,0.4)"}),r.addEventListener("mouseleave",()=>{r.style.transform="scale(1)",r.style.boxShadow="0 2px 8px rgba(0,0,0,0.3)"})};i(s),i(t);let o=()=>{let r=this.cache.size;this.cache.clear(),localStorage.removeItem("supernal-tts-cache"),s.innerHTML=`<span>\u2713</span><span>Cleared ${r}</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 ${r} cached items`)};s.addEventListener("click",r=>{r.preventDefault();let a=r.clientX-r.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",r=>{r.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:i,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(i);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(i,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}},i={"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?(i.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:i,body:JSON.stringify(s)});if(!n.ok){let a=await n.json().catch(()=>({}));throw new Error(a.error||`HTTP ${n.status}: ${n.statusText}`)}let r=await n.json();return{audioUrl:`${this.apiUrl}${r.audioUrl}`,hash:r.hash,cached:r.cached,duration:r.duration,cost:r.cost}}catch(n){if(console.error("TTS generation failed:",n),n.message.includes("Failed to fetch")||n.message.includes("NetworkError")){let r=n.message;throw new Error(`TTS API unreachable: ${r}. Check if ${this.apiUrl} is accessible.`)}throw n}}async generateAudioProgressive(e,t,s,i){let o={text:e,options:{provider:t.provider,voice:t.voice,speed:t.speed||1}},n={"Content-Type":"application/json",Accept:"text/event-stream"},r=t.apiKey||this.apiKey;r&&(n.Authorization=`Bearer ${r}`);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,p="",g=0,v=0,f=(c,m)=>{if(i){let T=c/m*100,y=i.querySelector(".supernal-tts-progress-slider");y&&(y.value=T.toString());let u=i.querySelector(".current");u&&(u.textContent=`Chunk ${c}/${m}`)}};for(;;){let{done:c,value:m}=await l.read();if(c)break;p+=d.decode(m,{stream:!0});let T=p.split(`
93
+ `;let n=r=>{r.addEventListener("mouseenter",()=>{r.style.transform="scale(1.05)",r.style.boxShadow="0 4px 12px rgba(0,0,0,0.4)"}),r.addEventListener("mouseleave",()=>{r.style.transform="scale(1)",r.style.boxShadow="0 2px 8px rgba(0,0,0,0.3)"})};n(s),n(t);let o=()=>{let r=this.cache.size;this.cache.clear(),localStorage.removeItem("supernal-tts-cache"),s.innerHTML=`<span>\u2713</span><span>Cleared ${r}</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 ${r} cached items`)};s.addEventListener("click",r=>{r.preventDefault();let a=r.clientX-r.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",r=>{r.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:n,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 i=this.getCachedAudioUrl(n);if(!i)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);i=a.audioUrl.startsWith("http")?a.audioUrl:`${this.apiUrl}${a.audioUrl}`,this.cacheAudioUrl(n,i,a)}await this.playAudio(i,e,o)}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 generateAudio(e,t){let s={text:e,options:{provider:t.provider,voice:t.voice,speed:t.speed||1}},n={"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?(n.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 i=await fetch(`${this.apiUrl}/api/v1/generate`,{method:"POST",headers:n,body:JSON.stringify(s)});if(!i.ok){let a=await i.json().catch(()=>({}));throw new Error(a.error||`HTTP ${i.status}: ${i.statusText}`)}let r=await i.json();return{audioUrl:`${this.apiUrl}${r.audioUrl}`,hash:r.hash,cached:r.cached,duration:r.duration,cost:r.cost}}catch(i){if(console.error("TTS generation failed:",i),i.message.includes("Failed to fetch")||i.message.includes("NetworkError")){let r=i.message;throw new Error(`TTS API unreachable: ${r}. Check if ${this.apiUrl} is accessible.`)}throw i}}async generateAudioProgressive(e,t,s,n){let o={text:e,options:{provider:t.provider,voice:t.voice,speed:t.speed||1}},i={"Content-Type":"application/json",Accept:"text/event-stream"},r=t.apiKey||this.apiKey;r&&(i.Authorization=`Bearer ${r}`);try{let a=await fetch(`${this.apiUrl}/api/v1/generate-progressive`,{method:"POST",headers:i,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,p="",h=0,v=0,f=(c,m)=>{if(n){let T=c/m*100,y=n.querySelector(".supernal-tts-progress-slider");y&&(y.value=T.toString());let u=n.querySelector(".current");u&&(u.textContent=`Chunk ${c}/${m}`)}};for(;;){let{done:c,value:m}=await l.read();if(c)break;p+=d.decode(m,{stream:!0});let T=p.split(`
94
94
 
95
- `);p=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 h=u.audioUrl.startsWith("http")?u.audioUrl:`${this.apiUrl}${u.audioUrl}`;this.cacheAudioUrl(t.hash,h,{hash:u.hash,cached:!1,duration:u.duration,cost:u.cost}),await this.playAudio(h,s,i);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,i){return new Promise((o,n)=>{let r=new Audio(e);this.currentAudio=r,this.currentButton=t,this.clientSideSpeed&&(r.preservesPitch=!0,r.mozPreservesPitch=!0,r.webkitPreservesPitch=!0),i!==void 0&&this.clientSideSpeed&&(r.playbackRate=i),this.progressUpdateInterval&&(clearInterval(this.progressUpdateInterval),this.progressUpdateInterval=null),r.addEventListener("loadstart",()=>{this.currentAudio===r&&this.setButtonState(t,"loading")}),r.addEventListener("canplay",()=>{if(this.currentAudio===r){this.setButtonState(t,"playing"),this.isProcessing=!1;let a=r.play();a!==void 0&&a.catch(l=>{console.error("Audio play failed:",l),this.currentAudio===r&&(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(r,s)},100))}}),r.addEventListener("ended",()=>{if(this.currentAudio===r){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()}}),r.addEventListener("error",a=>{this.currentAudio===r&&(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")))}),r.load()})}updateProgressBar(e,t){if(!e||!t)return;let s=t.querySelector(".supernal-tts-progress-slider"),i=t.querySelector(".current"),o=t.querySelector(".duration");if(s&&!isNaN(e.duration)){let n=e.currentTime/e.duration*100;s.value=n.toString()}i&&(i.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,i){let{hash:o,progressBar:n}=s;if(!this.isProcessing)try{this.isProcessing=!0,this.setButtonState(e,"loading");let r=this.getCachedAudioUrl(o);if(!r){let a=await this.generateAudio(t,s);r=a.audioUrl.startsWith("http")?a.audioUrl:`${this.apiUrl}${a.audioUrl}`,this.cacheAudioUrl(o,r,a)}await this.playAudioFromPosition(r,e,n,i)}catch(r){console.error("TTS Error:",r),this.setButtonState(e,"error"),this.showError(e,r.message),setTimeout(()=>this.setButtonState(e,"ready"),3e3),this.isProcessing=!1}}async playAudioFromPosition(e,t,s,i){return new Promise((o,n)=>{let r=new Audio(e);this.currentAudio=r,this.currentButton=t,this.clientSideSpeed&&(r.preservesPitch=!0,r.mozPreservesPitch=!0,r.webkitPreservesPitch=!0),this.progressUpdateInterval&&(clearInterval(this.progressUpdateInterval),this.progressUpdateInterval=null);let a=!1;r.addEventListener("loadstart",()=>{this.currentAudio===r&&this.setButtonState(t,"loading")}),r.addEventListener("loadedmetadata",()=>{this.currentAudio===r&&!a&&i>0&&(r.currentTime=r.duration*i,a=!0)}),r.addEventListener("canplay",()=>{if(this.currentAudio===r){!a&&i>0&&r.duration>0&&(r.currentTime=r.duration*i,a=!0),this.setButtonState(t,"playing"),this.isProcessing=!1;let l=r.play();l!==void 0&&l.catch(d=>{console.error("Audio play failed:",d),this.currentAudio===r&&(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(r,s)},100))}}),r.addEventListener("ended",()=>{if(this.currentAudio===r){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()}}),r.addEventListener("error",l=>{this.currentAudio===r&&(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")))}),r.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"),i=e.querySelector(".supernal-tts-text");switch(t){case"loading":e.classList.add("supernal-tts-loading"),e.disabled=!0,i&&(i.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"/>'),i&&(i.textContent="Pause");break;case"paused":e.classList.add("supernal-tts-paused"),e.disabled=!1,s&&(s.innerHTML='<path d="M8 5v14l11-7z"/>'),i&&(i.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"/>'),i&&(i.textContent="Error");break;default:e.disabled=!1,s&&(s.innerHTML='<path d="M8 5v14l11-7z"/>'),i&&(i.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),i=0;for(let o=0;o<s.length;o++){let n=s.charCodeAt(o);i=(i<<5)-i+n,i=i&i}return Math.abs(i).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 S(e),window.SupernalTTSInstance)}static getInstance(){return window.SupernalTTSInstance}addWidget(e,t,s={}){let i=null;if(typeof e=="string"?i=document.querySelector(e):i=e,!i){console.error("TTS Widget: Element not found");return}i.classList.add("supernal-tts-widget"),i.dataset.text=t,s.voice&&(i.dataset.voice=s.voice),s.provider&&(i.dataset.provider=s.provider),this.setupWidget(i)}};if(typeof window<"u")if(window.SupernalTTS)console.debug("SupernalTTS already loaded, skipping redeclaration");else{window.SupernalTTS=L;let S=document.querySelector("script[data-supernal-tts-auto-init]");if(S&&S.dataset){let e=JSON.parse(S.dataset.supernalTtsAutoInit||"{}");L.init(e)}}B=L});var b=class{static{this.instance=null}static{this.loadPromise=null}static async load(e={}){if(this.instance)return this.instance;if(this.loadPromise)return this.loadPromise;let{version:t="major",mode:s="auto",cdnUrl:i="https://unpkg.com/@supernal/tts-widget",timeout:o=3e3,onCdnFail:n,onCdnSuccess:r}=e;return this.loadPromise=this.loadWidget({version:t,mode:s,cdnUrl:i,timeout:o,onCdnFail:n,onCdnSuccess:r}),this.loadPromise}static async loadWidget(e){let{mode:t,version:s,cdnUrl:i,timeout:o,onCdnFail:n,onCdnSuccess:r}=e;if(t==="bundled")return console.log("[TTS Widget] Loading bundled version (mode: bundled)"),this.loadBundled();if(t==="cdn"||t==="auto")try{console.log("[TTS Widget] Attempting CDN load...");let a=await this.loadFromCdn(i,s,o);return console.log("[TTS Widget] CDN load successful"),r?.(),this.instance=a,a}catch(a){let l=a instanceof Error?a.message:String(a);if(console.warn("[TTS Widget] CDN load failed:",l),n?.(a instanceof Error?a:new Error(l)),t==="cdn")throw new Error(`Failed to load widget from CDN: ${l}`);return console.log("[TTS Widget] Falling back to bundled version"),this.loadBundled()}return this.loadBundled()}static async loadFromCdn(e,t,s){let i=this.resolveVersionPath(t),o=`${e}${i}/dist/widget.js`;console.log(`[TTS Widget] Loading from CDN: ${o}`);let n=new AbortController,r=setTimeout(()=>n.abort(),s);try{let a=import(o),l=new Promise((p,g)=>{setTimeout(()=>g(new Error(`CDN load timeout after ${s}ms`)),s)}),d=await Promise.race([a,l]);if(clearTimeout(r),!d||!d.SupernalTTS)throw new Error("CDN module missing SupernalTTS export");return this.instance=d.SupernalTTS,this.instance}catch(a){throw clearTimeout(r),a}}static async loadBundled(){try{let e=await Promise.resolve().then(()=>(C(),A));if(!e||!e.SupernalTTS)throw new Error("Bundled widget missing SupernalTTS export");return this.instance=e.SupernalTTS,this.instance}catch(e){let t=e instanceof Error?e.message:String(e);throw new Error(`Failed to load bundled widget: ${t}`)}}static resolveVersionPath(e){return e==="major"?`@${this.getMajorVersion()}`:e==="latest"?"@latest":`@${e}`}static getMajorVersion(){{let e="1.3.1".match(/^(\d+)\./);if(e)return e[1]}return"1"}static reset(){this.instance=null,this.loadPromise=null}static getInstance(){return this.instance}},I=b;export{b as WidgetLoader,I as default};
95
+ `);p=T.pop()||"";for(let y of T)if(y.startsWith("data: "))try{let u=JSON.parse(y.substring(6));switch(u.type){case"started":h=u.totalChunks,console.log(`[Progressive TTS] Started: ${h} chunks`),this.setButtonState(s,"loading");break;case"progress":v=u.completed,console.log(`[Progressive TTS] Progress: ${v}/${h}`),f(v,h);break;case"complete":console.log("[Progressive TTS] Complete:",u);let g=u.audioUrl.startsWith("http")?u.audioUrl:`${this.apiUrl}${u.audioUrl}`;this.cacheAudioUrl(t.hash,g,{hash:u.hash,cached:!1,duration:u.duration,cost:u.cost}),await this.playAudio(g,s,n);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,n){return new Promise((o,i)=>{let r=new Audio(e);this.currentAudio=r,this.currentButton=t,this.clientSideSpeed&&(r.preservesPitch=!0,r.mozPreservesPitch=!0,r.webkitPreservesPitch=!0),n!==void 0&&this.clientSideSpeed&&(r.playbackRate=n),this.progressUpdateInterval&&(clearInterval(this.progressUpdateInterval),this.progressUpdateInterval=null),r.addEventListener("loadstart",()=>{this.currentAudio===r&&this.setButtonState(t,"loading")}),r.addEventListener("canplay",()=>{if(this.currentAudio===r){this.setButtonState(t,"playing"),this.isProcessing=!1;let a=r.play();a!==void 0&&a.catch(l=>{console.error("Audio play failed:",l),this.currentAudio===r&&(this.setButtonState(t,"error"),this.currentAudio=null,this.currentButton=null,this.isProcessing=!1,i(new Error("Audio playback failed")))}),s&&(s.classList.add("playing"),this.progressUpdateInterval=window.setInterval(()=>{this.updateProgressBar(r,s)},100))}}),r.addEventListener("ended",()=>{if(this.currentAudio===r){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()}}),r.addEventListener("error",a=>{this.currentAudio===r&&(this.setButtonState(t,"error"),this.currentAudio=null,this.currentButton=null,this.progressUpdateInterval&&(clearInterval(this.progressUpdateInterval),this.progressUpdateInterval=null),s&&s.classList.remove("playing"),i(new Error("Audio playback failed")))}),r.load()})}updateProgressBar(e,t){if(!e||!t)return;let s=t.querySelector(".supernal-tts-progress-slider"),n=t.querySelector(".current"),o=t.querySelector(".duration");if(s&&!isNaN(e.duration)){let i=e.currentTime/e.duration*100;s.value=i.toString()}n&&(n.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,n){let{hash:o,progressBar:i}=s;if(!this.isProcessing)try{this.isProcessing=!0,this.setButtonState(e,"loading");let r=this.getCachedAudioUrl(o);if(!r){let a=await this.generateAudio(t,s);r=a.audioUrl.startsWith("http")?a.audioUrl:`${this.apiUrl}${a.audioUrl}`,this.cacheAudioUrl(o,r,a)}await this.playAudioFromPosition(r,e,i,n)}catch(r){console.error("TTS Error:",r),this.setButtonState(e,"error"),this.showError(e,r.message),setTimeout(()=>this.setButtonState(e,"ready"),3e3),this.isProcessing=!1}}async playAudioFromPosition(e,t,s,n){return new Promise((o,i)=>{let r=new Audio(e);this.currentAudio=r,this.currentButton=t,this.clientSideSpeed&&(r.preservesPitch=!0,r.mozPreservesPitch=!0,r.webkitPreservesPitch=!0),this.progressUpdateInterval&&(clearInterval(this.progressUpdateInterval),this.progressUpdateInterval=null);let a=!1;r.addEventListener("loadstart",()=>{this.currentAudio===r&&this.setButtonState(t,"loading")}),r.addEventListener("loadedmetadata",()=>{this.currentAudio===r&&!a&&n>0&&(r.currentTime=r.duration*n,a=!0)}),r.addEventListener("canplay",()=>{if(this.currentAudio===r){!a&&n>0&&r.duration>0&&(r.currentTime=r.duration*n,a=!0),this.setButtonState(t,"playing"),this.isProcessing=!1;let l=r.play();l!==void 0&&l.catch(d=>{console.error("Audio play failed:",d),this.currentAudio===r&&(this.setButtonState(t,"error"),this.currentAudio=null,this.currentButton=null,this.isProcessing=!1,i(new Error("Audio playback failed")))}),s&&(s.classList.add("playing"),this.progressUpdateInterval=window.setInterval(()=>{this.updateProgressBar(r,s)},100))}}),r.addEventListener("ended",()=>{if(this.currentAudio===r){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()}}),r.addEventListener("error",l=>{this.currentAudio===r&&(this.setButtonState(t,"error"),this.currentAudio=null,this.currentButton=null,this.progressUpdateInterval&&(clearInterval(this.progressUpdateInterval),this.progressUpdateInterval=null),s&&s.classList.remove("playing"),i(new Error("Audio playback failed")))}),r.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"),n=e.querySelector(".supernal-tts-text");switch(t){case"loading":e.classList.add("supernal-tts-loading"),e.disabled=!0,n&&(n.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"/>'),n&&(n.textContent="Pause");break;case"paused":e.classList.add("supernal-tts-paused"),e.disabled=!1,s&&(s.innerHTML='<path d="M8 5v14l11-7z"/>'),n&&(n.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"/>'),n&&(n.textContent="Error");break;default:e.disabled=!1,s&&(s.innerHTML='<path d="M8 5v14l11-7z"/>'),n&&(n.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),n=0;for(let o=0;o<s.length;o++){let i=s.charCodeAt(o);n=(n<<5)-n+i,n=n&n}return Math.abs(n).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 S(e),window.SupernalTTSInstance)}static getInstance(){return window.SupernalTTSInstance}addWidget(e,t,s={}){let n=null;if(typeof e=="string"?n=document.querySelector(e):n=e,!n){console.error("TTS Widget: Element not found");return}n.classList.add("supernal-tts-widget"),n.dataset.text=this.scrubText(t),s.voice&&(n.dataset.voice=s.voice),s.provider&&(n.dataset.provider=s.provider),this.setupWidget(n)}};if(typeof window<"u")if(window.SupernalTTS)console.debug("SupernalTTS already loaded, skipping redeclaration");else{window.SupernalTTS=L;let S=()=>{if(!window.SupernalTTSInstance){let s=document.querySelector("script[data-supernal-tts-auto-init]");if(s&&s.dataset){let n=JSON.parse(s.dataset.supernalTtsAutoInit||"{}");L.init(n)}}let t=document.querySelectorAll(".supernal-tts-widget");if(t.length>0&&window.SupernalTTSInstance){let s=window.SupernalTTSInstance;t.forEach(n=>{n.classList.contains("supernal-tts-widget-initialized")||s.setupWidget(n)})}};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",S):S()}B=L});var b=class{static{this.instance=null}static{this.loadPromise=null}static async load(e={}){if(this.instance)return this.instance;if(this.loadPromise)return this.loadPromise;let{version:t="major",mode:s="auto",cdnUrl:n="https://unpkg.com/@supernal/tts-widget",timeout:o=3e3,onCdnFail:i,onCdnSuccess:r}=e;return this.loadPromise=this.loadWidget({version:t,mode:s,cdnUrl:n,timeout:o,onCdnFail:i,onCdnSuccess:r}),this.loadPromise}static async loadWidget(e){let{mode:t,version:s,cdnUrl:n,timeout:o,onCdnFail:i,onCdnSuccess:r}=e;if(t==="bundled")return console.log("[TTS Widget] Loading bundled version (mode: bundled)"),this.loadBundled();if(t==="cdn"||t==="auto")try{console.log("[TTS Widget] Attempting CDN load...");let a=await this.loadFromCdn(n,s,o);return console.log("[TTS Widget] CDN load successful"),r?.(),this.instance=a,a}catch(a){let l=a instanceof Error?a.message:String(a);if(console.warn("[TTS Widget] CDN load failed:",l),i?.(a instanceof Error?a:new Error(l)),t==="cdn")throw new Error(`Failed to load widget from CDN: ${l}`);return console.log("[TTS Widget] Falling back to bundled version"),this.loadBundled()}return this.loadBundled()}static async loadFromCdn(e,t,s){let n=this.resolveVersionPath(t),o=`${e}${n}/dist/widget.js`;console.log(`[TTS Widget] Loading from CDN: ${o}`);let i=new AbortController,r=setTimeout(()=>i.abort(),s);try{let a=import(o),l=new Promise((p,h)=>{setTimeout(()=>h(new Error(`CDN load timeout after ${s}ms`)),s)}),d=await Promise.race([a,l]);if(clearTimeout(r),!d||!d.SupernalTTS)throw new Error("CDN module missing SupernalTTS export");return this.instance=d.SupernalTTS,this.instance}catch(a){throw clearTimeout(r),a}}static async loadBundled(){try{let e=await Promise.resolve().then(()=>(C(),A));if(!e||!e.SupernalTTS)throw new Error("Bundled widget missing SupernalTTS export");return this.instance=e.SupernalTTS,this.instance}catch(e){let t=e instanceof Error?e.message:String(e);throw new Error(`Failed to load bundled widget: ${t}`)}}static resolveVersionPath(e){return e==="major"?`@${this.getMajorVersion()}`:e==="latest"?"@latest":`@${e}`}static getMajorVersion(){{let e="1.3.1".match(/^(\d+)\./);if(e)return e[1]}return"1"}static reset(){this.instance=null,this.loadPromise=null}static getInstance(){return this.instance}},I=b;export{b as WidgetLoader,I as default};
96
96
  //# sourceMappingURL=loader.js.map