@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 +39 -1
- package/dist/loader.js +22 -22
- package/dist/loader.js.map +3 -3
- package/dist/widget.d.ts +11 -0
- package/dist/widget.d.ts.map +1 -1
- package/dist/widget.js +6 -6
- package/dist/widget.js.map +3 -3
- package/package.json +1 -1
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
|
|
2
|
-
<option value="alloy" ${
|
|
3
|
-
<option value="echo" ${
|
|
4
|
-
<option value="fable" ${
|
|
5
|
-
<option value="onyx" ${
|
|
6
|
-
<option value="nova" ${
|
|
7
|
-
<option value="shimmer" ${
|
|
8
|
-
<option value="coral" ${
|
|
9
|
-
`,d.appendChild(p),d.appendChild(
|
|
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),
|
|
13
|
-
<option value="alloy" ${
|
|
14
|
-
<option value="echo" ${
|
|
15
|
-
<option value="fable" ${
|
|
16
|
-
<option value="onyx" ${
|
|
17
|
-
<option value="nova" ${
|
|
18
|
-
<option value="shimmer" ${
|
|
19
|
-
<option value="coral" ${
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|