retiam 2.0.1 → 2.0.3

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.
Files changed (63) hide show
  1. package/THIRD_PARTY_NOTICES.md +3 -3
  2. package/agent/AGENTS.md +70 -0
  3. package/agent/CLAUDE.md +2 -0
  4. package/agent/skill/SKILL.md +114 -0
  5. package/agent/skill/examples/AreaLesson.ts +48 -0
  6. package/agent/skill/examples/ExplainMotion.ts +32 -0
  7. package/agent/skill/examples/NarratedMotion.ts +50 -0
  8. package/agent/skill/examples/SecantToTangent.ts +142 -0
  9. package/agent/skill/references/api.md +111 -0
  10. package/agent/skill/references/explainers.md +123 -0
  11. package/agent/skill/references/quality.md +151 -0
  12. package/agent/skill/references/styles.md +58 -0
  13. package/agent/skill/references/tts.md +166 -0
  14. package/agent/templates/explainer/lesson.json +25 -0
  15. package/agent/templates/explainer/review.json +6 -0
  16. package/agent/templates/explainer/styles.json +92 -0
  17. package/bin/agent-kit.js +26 -0
  18. package/bin/cli.js +24 -4
  19. package/bin/context.js +57 -0
  20. package/bin/init.js +71 -12
  21. package/bin/lesson-project.js +30 -0
  22. package/bin/lesson.js +195 -0
  23. package/bin/lint.js +6 -6
  24. package/bin/snapshot-runtime.js +185 -0
  25. package/bin/snapshot.js +32 -225
  26. package/dist/THIRD_PARTY_NOTICES.md +3 -3
  27. package/dist/UTIF-D6vx2UEX.min.js +1 -0
  28. package/dist/{UTIF-2Ear_0Uo.js → UTIF-mkffT70R.js} +1 -1
  29. package/dist/decompress-D7LzbvPD.min.js +15 -0
  30. package/dist/{index-B2sJBy4c.js → index-CdBPrYbr.js} +3830 -3795
  31. package/dist/index-jgFyrc2D.min.js +10472 -0
  32. package/dist/libs/explainer/example.d.ts +3 -0
  33. package/dist/libs/explainer/index.d.ts +5 -0
  34. package/dist/libs/explainer/index.js +135 -0
  35. package/dist/libs/explainer/plan.d.ts +12 -0
  36. package/dist/libs/explainer/plan.js +219 -0
  37. package/dist/libs/explainer/runtime.d.ts +24 -0
  38. package/dist/libs/explainer/styles.d.ts +3 -0
  39. package/dist/libs/explainer/types.d.ts +124 -0
  40. package/dist/libs/explainer/visuals.d.ts +18 -0
  41. package/dist/libs/tts/animations/Speak.d.ts +13 -7
  42. package/dist/libs/tts/components/TTSNarrationComponent.d.ts +9 -7
  43. package/dist/libs/tts/core/TTSCache.d.ts +3 -3
  44. package/dist/libs/tts/core/TTSClient.d.ts +3 -3
  45. package/dist/libs/tts/core/types.d.ts +48 -6
  46. package/dist/libs/tts/index.d.ts +2 -1
  47. package/dist/libs/tts/index.js +203 -1333
  48. package/dist/libs/tts/node/index.d.ts +10 -0
  49. package/dist/libs/tts/node/index.js +791 -0
  50. package/dist/libs/tts/providers/ElevenLabsTTSProvider.d.ts +30 -0
  51. package/dist/libs/tts/providers/ITTSProvider.d.ts +2 -2
  52. package/dist/libs/vite/index.d.ts +28 -0
  53. package/dist/libs/vite/index.js +158 -0
  54. package/dist/main-Cc4QQbSK.min.js +5939 -0
  55. package/dist/{main-D_G4uIju.js → main-DMu_5Qy7.js} +1 -1
  56. package/dist/msEdgeV2-D3w4Xsn-.js +642 -0
  57. package/dist/opentype.module-B-1cYAb-.min.js +1 -0
  58. package/dist/retiam.es.js +1 -1
  59. package/dist/retiam.es.min.js +1 -0
  60. package/dist/src/engine/animation/Animation.d.ts +1 -0
  61. package/dist/src/engine/entities/composites/text/mathjaxInterop.d.ts +6 -0
  62. package/package.json +15 -8
  63. package/readme.md +27 -7
@@ -1,1170 +1,12 @@
1
- import { Component as te, Color as C, hex as w, VGroup as ie, SDFText as ae, CENTER as re, Rect as se, Animation as ne } from "retiam";
1
+ import { TTS as p } from "./node/index.js";
2
+ import { ElevenLabsTTSProvider as W, MsEdgeTTSProvider as A, TTSAudio as D, TTSCache as L, TTSClient as H, encodeWAV as P } from "./node/index.js";
3
+ import { D as I, M as N, f as Y, a as X, g as Z, b as q, p as G, s as U } from "../../msEdgeV2-D3w4Xsn-.js";
4
+ import { Component as x, Color as d, hex as a, VGroup as b, SDFText as m, CENTER as S, Rect as g, Animation as C } from "retiam";
2
5
  /*! Retiam: noncommercial use only unless separately licensed. See ../../LICENSE and ../../COMMERCIAL_LICENSE.md. Third-party notices: ../../THIRD_PARTY_NOTICES.md and ../../THIRD_PARTY_LICENSES.txt. */
3
- function oe(a, e = 24e3) {
4
- const s = e * 2, n = a.length * 2, l = new ArrayBuffer(44 + n), o = new DataView(l);
5
- D(o, 0, "RIFF"), o.setUint32(4, 36 + n, !0), D(o, 8, "WAVE"), D(o, 12, "fmt "), o.setUint32(16, 16, !0), o.setUint16(20, 1, !0), o.setUint16(22, 1, !0), o.setUint32(24, e, !0), o.setUint32(28, s, !0), o.setUint16(32, 2, !0), o.setUint16(34, 16, !0), D(o, 36, "data"), o.setUint32(40, n, !0);
6
- let u = 44;
7
- for (let d = 0; d < a.length; d++) {
8
- const c = Math.max(-1, Math.min(1, a[d])), f = c < 0 ? c * 32768 : c * 32767;
9
- o.setInt16(u, f, !0), u += 2;
10
- }
11
- return l;
12
- }
13
- function D(a, e, t) {
14
- for (let i = 0; i < t.length; i++)
15
- a.setUint8(e + i, t.charCodeAt(i));
16
- }
17
- class G {
18
- text;
19
- phonemes;
20
- samples;
21
- sampleRate;
22
- duration;
23
- durationMs;
24
- voice;
25
- speed;
26
- wordBoundaries;
27
- rawBuffer;
28
- mimeType;
29
- _wavBuffer = null;
30
- _blob = null;
31
- _objectUrl = null;
32
- _dataUrl = null;
33
- constructor(e) {
34
- this.text = e.text, this.phonemes = e.phonemes || "", this.samples = e.samples ?? new Float32Array(0), this.sampleRate = e.sampleRate ?? 24e3, this.voice = e.voice ?? "af_heart", this.speed = e.speed ?? 1, this.wordBoundaries = e.wordBoundaries, this.rawBuffer = e.rawBuffer, this.mimeType = e.mimeType || (e.rawBuffer ? "audio/mpeg" : "audio/wav"), e.durationMs !== void 0 ? (this.durationMs = e.durationMs, this.duration = e.duration ?? e.durationMs / 1e3) : e.duration !== void 0 ? (this.duration = e.duration, this.durationMs = e.duration * 1e3) : (this.duration = this.samples.length > 0 ? this.samples.length / this.sampleRate : 0, this.durationMs = this.duration * 1e3);
35
- }
36
- /**
37
- * Converts raw PCM samples or decodes underlying rawBuffer into a Web Audio [[AudioBuffer]].
38
- */
39
- async getOrDecodeAudioBuffer(e) {
40
- if (this.samples && this.samples.length > 0)
41
- return this.toAudioBuffer(e);
42
- if (this.rawBuffer)
43
- try {
44
- const t = await e.decodeAudioData(this.rawBuffer.slice(0));
45
- return this.samples = t.getChannelData(0), this.sampleRate = t.sampleRate, t;
46
- } catch (t) {
47
- console.warn("[TTSAudio] Failed to decode rawBuffer via context.decodeAudioData:", t);
48
- }
49
- return this.toAudioBuffer(e);
50
- }
51
- /**
52
- * Converts raw PCM samples to a Web Audio [[AudioBuffer]].
53
- */
54
- toAudioBuffer(e) {
55
- const t = e || (typeof window < "u" ? window.__retiam_audio_context : null);
56
- if (t) {
57
- const r = t.createBuffer(1, Math.max(1, this.samples.length), this.sampleRate);
58
- return r.getChannelData(0).set(this.samples), r;
59
- }
60
- const i = typeof window < "u" ? window.AudioContext || window.webkitAudioContext : null;
61
- if (i) {
62
- const s = new i().createBuffer(1, Math.max(1, this.samples.length), this.sampleRate);
63
- return s.getChannelData(0).set(this.samples), s;
64
- }
65
- throw new Error("[TTSAudio] Cannot create AudioBuffer: No AudioContext available in this environment.");
66
- }
67
- /**
68
- * Returns an ArrayBuffer containing encoded 16-bit PCM WAV audio.
69
- */
70
- toWav() {
71
- return this._wavBuffer || (this.rawBuffer && this.mimeType.includes("wav") ? this._wavBuffer = this.rawBuffer : this._wavBuffer = oe(this.samples, this.sampleRate)), this._wavBuffer;
72
- }
73
- /**
74
- * Returns a standard WAV or MP3 [[Blob]].
75
- */
76
- toBlob() {
77
- return this._blob || (this.rawBuffer ? this._blob = new Blob([this.rawBuffer], { type: this.mimeType }) : this._blob = new Blob([this.toWav()], { type: "audio/wav" })), this._blob;
78
- }
79
- /**
80
- * Generates a base64 Data URL ("data:audio/...;base64,...").
81
- */
82
- toDataUrl() {
83
- if (!this._dataUrl) {
84
- const e = this.rawBuffer ? new Uint8Array(this.rawBuffer) : new Uint8Array(this.toWav());
85
- let t = "";
86
- const i = e.byteLength;
87
- for (let s = 0; s < i; s++)
88
- t += String.fromCharCode(e[s]);
89
- const r = typeof btoa < "u" ? btoa(t) : Buffer.from(t, "binary").toString("base64");
90
- this._dataUrl = `data:${this.mimeType};base64,${r}`;
91
- }
92
- return this._dataUrl;
93
- }
94
- /**
95
- * Creates or returns a cached Object URL ("blob:...") for this audio.
96
- */
97
- toObjectUrl() {
98
- if (!this._objectUrl)
99
- if (typeof URL < "u" && typeof URL.createObjectURL == "function")
100
- this._objectUrl = URL.createObjectURL(this.toBlob());
101
- else
102
- return this.toDataUrl();
103
- return this._objectUrl;
104
- }
105
- /**
106
- * Revokes any generated Object URL to release memory.
107
- */
108
- revokeObjectUrl() {
109
- this._objectUrl && typeof URL < "u" && typeof URL.revokeObjectURL == "function" && (URL.revokeObjectURL(this._objectUrl), this._objectUrl = null);
110
- }
111
- /**
112
- * Triggers a browser download or writes out the audio file.
113
- */
114
- save(e) {
115
- const t = this.mimeType.includes("mpeg") || this.mimeType.includes("mp3") ? ".mp3" : ".wav", i = `speech${t}`, r = e ? e.includes(".") ? e : `${e}${t}` : i;
116
- if (typeof document < "u") {
117
- const s = this.toObjectUrl(), n = document.createElement("a");
118
- n.style.display = "none", n.href = s, n.download = r, document.body.appendChild(n), n.click(), setTimeout(() => {
119
- document.body.removeChild(n);
120
- }, 100);
121
- return;
122
- }
123
- try {
124
- const s = globalThis.require ? globalThis.require("fs") : null;
125
- if (s) {
126
- const n = this.rawBuffer ? Buffer.from(this.rawBuffer) : Buffer.from(this.toWav());
127
- s.writeFileSync(r, n);
128
- }
129
- } catch (s) {
130
- console.warn(`[TTSAudio] Unable to save file ${r}:`, s);
131
- }
132
- }
133
- /**
134
- * Plays the audio through Retiam's [[AudioManager]] on the dedicated voiceover bus
135
- * with automatic BGM ducking and timeline synchronization.
136
- */
137
- async play(e, t = {}) {
138
- let i;
139
- return e && (typeof e.playVoiceover == "function" ? i = e : e.audio && typeof e.audio.playVoiceover == "function" && (i = e.audio)), i ? i.playVoiceover(this, t) : (console.warn("[TTSAudio] No active AudioManager provided to play(). Creating standalone audio playback."), this._playStandalone(t));
140
- }
141
- /**
142
- * Standalone playback fallback if no Retiam scene/AudioManager is provided.
143
- */
144
- _playStandalone(e) {
145
- return new Promise((t) => {
146
- if (typeof Audio > "u") {
147
- t(this.durationMs);
148
- return;
149
- }
150
- const i = new Audio(this.toObjectUrl());
151
- i.volume = Math.max(0, Math.min(1, e.volume ?? 1)), i.onended = () => {
152
- const r = e.padEndMs ?? 100;
153
- setTimeout(() => t(this.durationMs + r), r);
154
- }, i.onerror = () => {
155
- t(this.durationMs);
156
- }, i.play().catch(() => {
157
- t(this.durationMs);
158
- });
159
- });
160
- }
161
- }
162
- const le = {
163
- 1: [44100, 48e3, 32e3],
164
- 2: [22050, 24e3, 16e3],
165
- "2.5": [11025, 12e3, 8e3]
166
- }, de = {
167
- V1L3: [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320],
168
- V2L3: [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160]
169
- };
170
- function H(a) {
171
- const e = a instanceof Uint8Array ? a : new Uint8Array(a), t = e.length;
172
- let i = 0, r = 0, s = 0, n = 24e3, l = 96, o = 1;
173
- for (t >= 10 && e[0] === 73 && e[1] === 68 && e[2] === 51 && (i = 10 + ((e[6] & 127) << 21 | (e[7] & 127) << 14 | (e[8] & 127) << 7 | e[9] & 127)); i < t - 4; ) {
174
- if (e[i] === 255 && (e[i + 1] & 224) === 224) {
175
- const d = e[i + 1], c = e[i + 2], f = e[i + 3], h = d >> 3 & 3, p = h === 3 ? "1" : h === 2 ? "2" : h === 0 ? "2.5" : null, b = d >> 1 & 3, m = c >> 4 & 15, _ = c >> 2 & 3, A = c >> 1 & 1, U = f >> 6 & 3;
176
- if (p && b === 1 && _ < 3 && m > 0 && m < 15) {
177
- const x = le[p][_], F = de[p === "1" ? "V1L3" : "V2L3"][m], z = F * 1e3, R = p === "1" ? 1152 : 576, ee = Math.floor(R * (z / 8) / x) + A;
178
- n = x, l = F, o = U === 3 ? 1 : 2, s += R, r++, i += ee;
179
- continue;
180
- }
181
- }
182
- i++;
183
- }
184
- if (r === 0 || n === 0) {
185
- const d = Math.max(0.1, t / 12e3);
186
- return {
187
- sampleRate: 24e3,
188
- frameCount: 1,
189
- totalSamples: Math.floor(d * 24e3),
190
- durationSec: d,
191
- durationMs: d * 1e3,
192
- bitrateKbps: 96,
193
- channels: 1
194
- };
195
- }
196
- const u = s / n;
197
- return {
198
- sampleRate: n,
199
- frameCount: r,
200
- totalSamples: s,
201
- durationSec: u,
202
- durationMs: u * 1e3,
203
- bitrateKbps: l,
204
- channels: o
205
- };
206
- }
207
- const K = {
208
- // --- American English (US) ---
209
- "en-US-JennyNeural": {
210
- id: "en-US-JennyNeural",
211
- name: "Jenny",
212
- language: "en-us",
213
- gender: "Female",
214
- traits: "Warm, Conversational, Natural, Clear",
215
- targetQuality: "A+",
216
- overallGrade: "A+",
217
- provider: "msedge"
218
- },
219
- "en-US-GuyNeural": {
220
- id: "en-US-GuyNeural",
221
- name: "Guy",
222
- language: "en-us",
223
- gender: "Male",
224
- traits: "Confident, Broadcast, Documentary, Instructional",
225
- targetQuality: "A+",
226
- overallGrade: "A+",
227
- provider: "msedge"
228
- },
229
- "en-US-AriaNeural": {
230
- id: "en-US-AriaNeural",
231
- name: "Aria",
232
- language: "en-us",
233
- gender: "Female",
234
- traits: "Dynamic, Highly Expressive, Conversational",
235
- targetQuality: "A+",
236
- overallGrade: "A",
237
- provider: "msedge"
238
- },
239
- "en-US-AndrewNeural": {
240
- id: "en-US-AndrewNeural",
241
- name: "Andrew",
242
- language: "en-us",
243
- gender: "Male",
244
- traits: "Warm, Confident, Authentic, Honest",
245
- targetQuality: "A+",
246
- overallGrade: "A+",
247
- provider: "msedge"
248
- },
249
- "en-US-AvaNeural": {
250
- id: "en-US-AvaNeural",
251
- name: "Ava",
252
- language: "en-us",
253
- gender: "Female",
254
- traits: "Expressive, Natural, Pleasant",
255
- targetQuality: "A+",
256
- overallGrade: "A+",
257
- provider: "msedge"
258
- },
259
- "en-US-EmmaNeural": {
260
- id: "en-US-EmmaNeural",
261
- name: "Emma",
262
- language: "en-us",
263
- gender: "Female",
264
- traits: "Clear, Friendly, Conversational",
265
- targetQuality: "A",
266
- overallGrade: "A",
267
- provider: "msedge"
268
- },
269
- "en-US-BrianNeural": {
270
- id: "en-US-BrianNeural",
271
- name: "Brian",
272
- language: "en-us",
273
- gender: "Male",
274
- traits: "Approachable, Articulate, Natural",
275
- targetQuality: "A",
276
- overallGrade: "A",
277
- provider: "msedge"
278
- },
279
- "en-US-ChristopherNeural": {
280
- id: "en-US-ChristopherNeural",
281
- name: "Christopher",
282
- language: "en-us",
283
- gender: "Male",
284
- traits: "Deep, Authoritative, Cinematic",
285
- targetQuality: "A",
286
- overallGrade: "A",
287
- provider: "msedge"
288
- },
289
- "en-US-EricNeural": {
290
- id: "en-US-EricNeural",
291
- name: "Eric",
292
- language: "en-us",
293
- gender: "Male",
294
- traits: "Friendly, Casual, Natural",
295
- targetQuality: "A-",
296
- overallGrade: "A-",
297
- provider: "msedge"
298
- },
299
- "en-US-MichelleNeural": {
300
- id: "en-US-MichelleNeural",
301
- name: "Michelle",
302
- language: "en-us",
303
- gender: "Female",
304
- traits: "Professional, Engaging, Clear",
305
- targetQuality: "A-",
306
- overallGrade: "A-",
307
- provider: "msedge"
308
- },
309
- "en-US-AnaNeural": {
310
- id: "en-US-AnaNeural",
311
- name: "Ana",
312
- language: "en-us",
313
- gender: "Female",
314
- traits: "Childlike, Cheerful, Animated",
315
- targetQuality: "B+",
316
- overallGrade: "B+",
317
- provider: "msedge"
318
- },
319
- "en-US-SteffanNeural": {
320
- id: "en-US-SteffanNeural",
321
- name: "Steffan",
322
- language: "en-us",
323
- gender: "Male",
324
- traits: "Warm, Calming, Soft",
325
- targetQuality: "A-",
326
- overallGrade: "A-",
327
- provider: "msedge"
328
- },
329
- // --- British English (UK) ---
330
- "en-GB-SoniaNeural": {
331
- id: "en-GB-SoniaNeural",
332
- name: "Sonia",
333
- language: "en-gb",
334
- gender: "Female",
335
- traits: "RP British, Polished, Professional, Clear",
336
- targetQuality: "A+",
337
- overallGrade: "A+",
338
- provider: "msedge"
339
- },
340
- "en-GB-RyanNeural": {
341
- id: "en-GB-RyanNeural",
342
- name: "Ryan",
343
- language: "en-gb",
344
- gender: "Male",
345
- traits: "Modern British, Natural, Confident",
346
- targetQuality: "A",
347
- overallGrade: "A",
348
- provider: "msedge"
349
- },
350
- "en-GB-LibbyNeural": {
351
- id: "en-GB-LibbyNeural",
352
- name: "Libby",
353
- language: "en-gb",
354
- gender: "Female",
355
- traits: "Storyteller, Warm British, Engaging",
356
- targetQuality: "A-",
357
- overallGrade: "A-",
358
- provider: "msedge"
359
- },
360
- "en-GB-ThomasNeural": {
361
- id: "en-GB-ThomasNeural",
362
- name: "Thomas",
363
- language: "en-gb",
364
- gender: "Male",
365
- traits: "Refined RP British, Documentary, Calm",
366
- targetQuality: "A",
367
- overallGrade: "A",
368
- provider: "msedge"
369
- },
370
- // --- Australian & Canadian English ---
371
- "en-AU-NatashaNeural": {
372
- id: "en-AU-NatashaNeural",
373
- name: "Natasha",
374
- language: "en-au",
375
- gender: "Female",
376
- traits: "Australian, Friendly, Energetic",
377
- targetQuality: "A",
378
- overallGrade: "A",
379
- provider: "msedge"
380
- },
381
- "en-AU-WilliamMultilingualNeural": {
382
- id: "en-AU-WilliamMultilingualNeural",
383
- name: "William",
384
- language: "en-au",
385
- gender: "Male",
386
- traits: "Australian Multilingual, Articulate",
387
- targetQuality: "A-",
388
- overallGrade: "A-",
389
- provider: "msedge"
390
- },
391
- "en-CA-ClaraNeural": {
392
- id: "en-CA-ClaraNeural",
393
- name: "Clara",
394
- language: "en-ca",
395
- gender: "Female",
396
- traits: "Canadian, Clear, Crisp",
397
- targetQuality: "A-",
398
- overallGrade: "A-",
399
- provider: "msedge"
400
- },
401
- "en-CA-LiamNeural": {
402
- id: "en-CA-LiamNeural",
403
- name: "Liam",
404
- language: "en-ca",
405
- gender: "Male",
406
- traits: "Canadian, Natural, Conversational",
407
- targetQuality: "A-",
408
- overallGrade: "A-",
409
- provider: "msedge"
410
- },
411
- // --- Indian English ---
412
- "en-IN-NeerjaNeural": {
413
- id: "en-IN-NeerjaNeural",
414
- name: "Neerja",
415
- language: "en-in",
416
- gender: "Female",
417
- traits: "Indian English, Professional, High Clarity",
418
- targetQuality: "A",
419
- overallGrade: "A",
420
- provider: "msedge"
421
- },
422
- "en-IN-PrabhatNeural": {
423
- id: "en-IN-PrabhatNeural",
424
- name: "Prabhat",
425
- language: "en-in",
426
- gender: "Male",
427
- traits: "Indian English, Confident, Instructional",
428
- targetQuality: "A",
429
- overallGrade: "A",
430
- provider: "msedge"
431
- },
432
- // --- Global Languages ---
433
- "es-ES-ElviraNeural": {
434
- id: "es-ES-ElviraNeural",
435
- name: "Elvira",
436
- language: "es-es",
437
- gender: "Female",
438
- traits: "Spanish (Spain), Natural, Clear",
439
- targetQuality: "A",
440
- overallGrade: "A",
441
- provider: "msedge"
442
- },
443
- "fr-FR-DeniseNeural": {
444
- id: "fr-FR-DeniseNeural",
445
- name: "Denise",
446
- language: "fr-fr",
447
- gender: "Female",
448
- traits: "French (France), Refined, Elegant",
449
- targetQuality: "A",
450
- overallGrade: "A",
451
- provider: "msedge"
452
- },
453
- "fr-FR-HenriNeural": {
454
- id: "fr-FR-HenriNeural",
455
- name: "Henri",
456
- language: "fr-fr",
457
- gender: "Male",
458
- traits: "French (France), Documentary, Authoritative",
459
- targetQuality: "A",
460
- overallGrade: "A",
461
- provider: "msedge"
462
- },
463
- "de-DE-KatjaNeural": {
464
- id: "de-DE-KatjaNeural",
465
- name: "Katja",
466
- language: "de-de",
467
- gender: "Female",
468
- traits: "German (Germany), Precise, Professional",
469
- targetQuality: "A",
470
- overallGrade: "A",
471
- provider: "msedge"
472
- },
473
- "de-DE-KillianNeural": {
474
- id: "de-DE-KillianNeural",
475
- name: "Killian",
476
- language: "de-de",
477
- gender: "Male",
478
- traits: "German (Germany), Clear, Calm",
479
- targetQuality: "A",
480
- overallGrade: "A",
481
- provider: "msedge"
482
- },
483
- "it-IT-ElsaNeural": {
484
- id: "it-IT-ElsaNeural",
485
- name: "Elsa",
486
- language: "it-it",
487
- gender: "Female",
488
- traits: "Italian, Melodic, Expressive",
489
- targetQuality: "A",
490
- overallGrade: "A",
491
- provider: "msedge"
492
- },
493
- "ja-JP-NanamiNeural": {
494
- id: "ja-JP-NanamiNeural",
495
- name: "Nanami",
496
- language: "ja-jp",
497
- gender: "Female",
498
- traits: "Japanese, Crisp, Anime/Modern",
499
- targetQuality: "A+",
500
- overallGrade: "A+",
501
- provider: "msedge"
502
- },
503
- "ja-JP-KeitaNeural": {
504
- id: "ja-JP-KeitaNeural",
505
- name: "Keita",
506
- language: "ja-jp",
507
- gender: "Male",
508
- traits: "Japanese, Natural, Instructional",
509
- targetQuality: "A",
510
- overallGrade: "A",
511
- provider: "msedge"
512
- },
513
- "zh-CN-XiaoxiaoNeural": {
514
- id: "zh-CN-XiaoxiaoNeural",
515
- name: "Xiaoxiao",
516
- language: "zh-cn",
517
- gender: "Female",
518
- traits: "Chinese Mandarin, Natural, Expressive, Smooth",
519
- targetQuality: "A+",
520
- overallGrade: "A+",
521
- provider: "msedge"
522
- },
523
- "zh-CN-YunxiNeural": {
524
- id: "zh-CN-YunxiNeural",
525
- name: "Yunxi",
526
- language: "zh-cn",
527
- gender: "Male",
528
- traits: "Chinese Mandarin, Energetic, Storytelling",
529
- targetQuality: "A",
530
- overallGrade: "A",
531
- provider: "msedge"
532
- }
533
- }, L = "en-US-JennyNeural";
534
- let B = null;
535
- function W() {
536
- return B && B.length > 0 ? B : Object.values(K);
537
- }
538
- function ce(a) {
539
- const e = a.toLowerCase().trim();
540
- return W().find(
541
- (i) => i.id.toLowerCase() === e || i.name.toLowerCase() === e || i.id.toLowerCase().includes(e) || i.name.toLowerCase().includes(e)
542
- );
543
- }
544
- function Te(a) {
545
- const e = a.toLowerCase().replace(/_/g, "-");
546
- return W().filter(
547
- (t) => t.language.toLowerCase() === e || t.language.toLowerCase().startsWith(e)
548
- );
549
- }
550
- function he(a) {
551
- B = a;
552
- }
553
- async function ue() {
554
- if (B)
555
- return B;
556
- const e = "https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list?trustedclienttoken=6A5AA1D4EAFF4E9FB37E23D68491D6F4";
557
- try {
558
- const t = await fetch(e);
559
- if (!t.ok)
560
- throw new Error(`HTTP ${t.status} ${t.statusText}`);
561
- const i = await t.json();
562
- if (Array.isArray(i)) {
563
- const r = i.map((s) => ({
564
- id: s.ShortName || s.Name,
565
- name: (s.FriendlyName || s.ShortName || "").replace(/^Microsoft\s+/, "").replace(/\s+Online\s+\(Natural\)/, ""),
566
- language: (s.Locale || "en-US").toLowerCase(),
567
- gender: s.Gender === "Male" ? "Male" : "Female",
568
- traits: s.VoiceTag?.VoicePersonalities ? s.VoiceTag.VoicePersonalities.join(", ") : void 0,
569
- targetQuality: "A",
570
- overallGrade: "A",
571
- provider: "msedge"
572
- }));
573
- return B = r, r;
574
- }
575
- } catch (t) {
576
- console.warn("[MsEdgeVoices] Unable to fetch remote voices list. Falling back to built-in curated catalog:", t);
577
- }
578
- return Object.values(K);
579
- }
580
- class T {
581
- maxSize;
582
- cache = /* @__PURE__ */ new Map();
583
- constructor(e = 128) {
584
- this.maxSize = Math.max(1, e);
585
- }
586
- static createKey(e, t, i) {
587
- return `${t}:${i.toFixed(2)}:${e.trim().toLowerCase()}`;
588
- }
589
- get(e, t, i) {
590
- const r = T.createKey(e, t, i), s = this.cache.get(r);
591
- if (s)
592
- return this.cache.delete(r), this.cache.set(r, s), s;
593
- }
594
- set(e, t, i, r) {
595
- const s = T.createKey(e, t, i);
596
- if (this.cache.has(s))
597
- this.cache.delete(s);
598
- else if (this.cache.size >= this.maxSize) {
599
- const n = this.cache.keys().next().value;
600
- n && (this.cache.get(n)?.revokeObjectUrl(), this.cache.delete(n));
601
- }
602
- this.cache.set(s, r);
603
- }
604
- has(e, t, i) {
605
- return this.cache.has(T.createKey(e, t, i));
606
- }
607
- clear() {
608
- for (const e of this.cache.values())
609
- e.revokeObjectUrl();
610
- this.cache.clear();
611
- }
612
- get size() {
613
- return this.cache.size;
614
- }
615
- }
616
- const J = "6A5AA1D4EAFF4E9FB37E23D68491D6F4", pe = "wss://speech.platform.bing.com/consumer/speech/synthesize/readaloud/edge/v1", fe = "2-155.0.4283.45", me = 11644473600, ge = [
617
- 1779033703,
618
- 3144134277,
619
- 1013904242,
620
- 2773480762,
621
- 1359893119,
622
- 2600822924,
623
- 528734635,
624
- 1541459225
625
- ], ye = [
626
- 4255104674,
627
- 2435071207,
628
- 3192492180,
629
- 3442351626,
630
- 1811967817,
631
- 79508907,
632
- 4272990180,
633
- 66059218
634
- ], _e = [
635
- 3945054110,
636
- 358115615,
637
- 2696978146,
638
- 217723578,
639
- 2787307148,
640
- 3520959633,
641
- 3235718560,
642
- 4192668342,
643
- 4039316851,
644
- 1884868174,
645
- 1904797190,
646
- 15051571,
647
- 3529027680,
648
- 1920361828
649
- ], I = [
650
- 1116352408,
651
- 1899447441,
652
- 3049323471,
653
- 3921009573,
654
- 961987163,
655
- 1508970993,
656
- 2453635748,
657
- 2870763221,
658
- 3624381080,
659
- 310598401,
660
- 607225278,
661
- 1426881987,
662
- 1925078388,
663
- 2162078206,
664
- 2614888103,
665
- 3248222580,
666
- 3835390401,
667
- 4022224774,
668
- 264347078,
669
- 604807628,
670
- 770255983,
671
- 1249150122,
672
- 1555081692,
673
- 1996064986,
674
- 2554220882,
675
- 2821834349,
676
- 2952996808,
677
- 3210313671,
678
- 3336571891,
679
- 3584528711,
680
- 113926993,
681
- 338241895,
682
- 666307205,
683
- 773529912,
684
- 1294757372,
685
- 1396182291,
686
- 1695183700,
687
- 1986661051,
688
- 2177026350,
689
- 2456956037,
690
- 2730485921,
691
- 2820302411,
692
- 3259730800,
693
- 3345764771,
694
- 3516065817,
695
- 3600352804,
696
- 4094571909,
697
- 275423344,
698
- 430227734,
699
- 506948616,
700
- 659060556,
701
- 883997877,
702
- 958139571,
703
- 1322822218,
704
- 1537002063,
705
- 1747873779,
706
- 1955562222,
707
- 2024104815,
708
- 2227730452,
709
- 2361852424,
710
- 2428436474,
711
- 2756734187,
712
- 3204031479,
713
- 3329325298
714
- ], X = [...I];
715
- X[14] = 3386640039;
716
- const Q = [
717
- [3895366065, 3005677776],
718
- [1155409094, 2296378653],
719
- [1005282748, 693190712],
720
- [3365689651, 3005795295],
721
- [2367603217, 3557143765],
722
- [827258325, 652566332],
723
- [3911487921, 1618477510],
724
- [4037392853, 2192753147],
725
- [101076211, 4199654362],
726
- [3665427040, 545047652],
727
- [3201434976, 1010595059],
728
- [2314848760, 3536931681],
729
- [905590982, 3137643201],
730
- [3410617286, 3311559015]
731
- ], S = (a) => a >>> 0, g = (...a) => S(a.reduce((e, t) => e + t, 0)), y = (a, e) => S(Math.imul(a, e)), v = (a) => S(a << 16 | a >>> 16), N = (a, e) => S(a >>> e | a << 32 - e), P = (a) => S(a >>> 7 ^ a << 25 ^ a >>> 18 ^ a << 14 ^ a >>> 3), V = (a) => S(a >>> 17 ^ a << 15 ^ a >>> 19 ^ a << 13 ^ a >>> 10), Se = (a, e, t) => S(t ^ a & (e ^ t)), be = (a, e, t) => S(a & e | t & (a | e)), we = (a) => S(N(a, 6) ^ N(a, 11) ^ N(a, 25)), ve = (a) => S(N(a, 2) ^ N(a, 13) ^ N(a, 22));
732
- function Y(a, e, t) {
733
- let [i, r, s, n, l, o, u, d] = a;
734
- const c = t === 14 ? X : I;
735
- for (let f = t; f < 64; f++) {
736
- const h = g(d, we(l), Se(l, o, u), c[f], e[f]), p = g(ve(i), be(i, r, s));
737
- [i, r, s, n, l, o, u, d] = [g(h, p), i, r, s, g(n, h), l, o, u];
738
- }
739
- return [i, r, s, n, l, o, u, d];
740
- }
741
- function q(a, e) {
742
- for (let t = e; t < 64; t++)
743
- a.push(g(
744
- V(a[t - 2]),
745
- a[t - 7],
746
- P(a[t - 15]),
747
- a[t - 16]
748
- ));
749
- }
750
- function Ae(a) {
751
- const e = new Array(16).fill(0);
752
- e[14] = S(a[0] << 16 | a[1] << 8 | a[2]), e[15] = S(a[3] << 24 | a[4] << 16 | a[5] << 8 | a[6]);
753
- for (let i = 16; i < 30; i++) {
754
- let r = _e[i - 16];
755
- if (i - 2 >= 14) {
756
- const s = V(e[i - 2]);
757
- r = g(r, i === 16 ? S(s ^ 758464) : s);
758
- }
759
- if (i - 7 >= 14 && (r = g(r, e[i - 7])), i - 15 >= 14) {
760
- const s = P(e[i - 15]);
761
- r = g(r, i === 29 ? S(s ^ 94112640) : s);
762
- }
763
- e.push(r);
764
- }
765
- e.push(g(
766
- V(e[28]),
767
- e[23],
768
- P(e[15]),
769
- e[14],
770
- 3386640039,
771
- -I[14]
772
- )), q(e, 31);
773
- const t = Y(ye, e, 14);
774
- return ge.map((i, r) => g(i, t[r]));
775
- }
776
- function ke(a) {
777
- const e = 57 + a.length, t = ((55 - e) % 64 + 64) % 64, i = new Uint8Array(a.length - 7 + 1 + t + 8);
778
- i.set(a.subarray(7)), i[a.length - 7] = 128;
779
- const r = e * 8, s = new DataView(i.buffer);
780
- s.setUint32(i.length - 8, Math.floor(r / 4294967296), !1), s.setUint32(i.length - 4, S(r), !1);
781
- let n = Ae(a.subarray(0, 7));
782
- for (let u = 0; u < i.length; u += 64) {
783
- const d = new DataView(i.buffer, i.byteOffset + u, 64), c = Array.from({ length: 16 }, (h, p) => d.getUint32(p * 4, !1));
784
- q(c, 16);
785
- const f = Y(n, c, 0);
786
- n = n.map((h, p) => g(h, f[p]));
787
- }
788
- const l = new Uint8Array(32), o = new DataView(l.buffer);
789
- return n.forEach((u, d) => o.setUint32(d * 4, u, !1)), l;
790
- }
791
- function Ce(a) {
792
- const e = new Uint8Array(Math.ceil(a.length / 4) * 4);
793
- e.set(a);
794
- const t = new DataView(e.buffer), i = Array.from({ length: e.length / 4 }, (r, s) => t.getUint32(s * 4, !0));
795
- return i.length % 2 !== 0 && i.push(0), i;
796
- }
797
- function xe(a, e) {
798
- const t = new DataView(e.buffer, e.byteOffset, e.byteLength), i = S(t.getUint32(0, !0) | 1), r = S(t.getUint32(4, !0) | 1), s = [y(i, 2190031155), y(r, 2190031155)], n = Q.map(([h], p) => y(h, s[p % 2])), l = Q.map(([, h]) => h), o = new Uint8Array(a.length + 1);
799
- o.set(a, 1), Ce(o).forEach((h, p) => {
800
- let b = y(h, p % 2 === 0 ? i : r), m = y(v(h), p % 2 === 0 ? 4010109435 : 3273069531);
801
- p === 0 && (b = g(b, y(4076407578, s[0])), m = g(m, 186253312)), n.push(b), l.push(m);
802
- });
803
- let u = 0, d = 0, c = 0, f = 0;
804
- for (let h = 0; h < n.length; h += 2) {
805
- const p = n[h], b = n[h + 1];
806
- let m = v(g(l[h], p)), _ = y(m, 1995771700);
807
- u = g(u, y(v(_), 3287280279), _), m = v(g(l[h + 1], b)), _ = y(m, 411160124), d = g(y(v(_), 901586633), _), u = g(u, d), c = g(y(c, i), p), c = y(v(c), 3482890513), c = y(v(c), 2265471903), c = y(v(c), 315537773), c = y(v(c), 629022083), f = g(f, c), m = g(y(c, r), b), m = y(v(m), 2725517045), m = y(v(m), 3548616447), c = y(v(y(v(m), 2090019721)), 3215236969), f = g(f, c);
808
- }
809
- return [S(d ^ c), S(u ^ f)];
810
- }
811
- function $(a) {
812
- return Array.from(a, (e) => e.toString(16).padStart(2, "0")).join("").toUpperCase();
813
- }
814
- function Ee(a = Date.now() / 1e3) {
815
- let e = a + me;
816
- e -= e % 300, e *= 1e7;
817
- const t = new TextEncoder().encode(`${e.toFixed(0)}${J}`), i = ke(t), r = xe(t, i), s = new Uint8Array(8), n = new DataView(s.buffer);
818
- return n.setUint32(0, r[0], !0), n.setUint32(4, r[1], !0), `${$(s)}-${$(i)}`;
819
- }
820
- function Be(a) {
821
- const e = a;
822
- e.__retiamV2Patched || (e.getSynthUrl = async () => {
823
- const t = Array.from({ length: 32 }, () => Math.floor(Math.random() * 16).toString(16)).join(""), i = Ee();
824
- return `${pe}?TrustedClientToken=${J}&Sec-MS-GEC=${i}&Sec-MS-GEC-Version=${fe}&ConnectionId=${t}`;
825
- }, e.__retiamV2Patched = !0);
826
- }
827
- class Me {
828
- name = "msedge-tts";
829
- _config;
830
- _cache;
831
- _isBrowser;
832
- _nodeClients = [];
833
- constructor(e = {}) {
834
- this._isBrowser = typeof window < "u" && typeof window.document < "u", this._config = {
835
- cacheAudio: e.cacheAudio !== !1,
836
- maxCacheSize: e.maxCacheSize ?? 128,
837
- msedgeEndpoint: e.msedgeEndpoint || "/api/tts/msedge"
838
- }, this._cache = new T(this._config.maxCacheSize);
839
- }
840
- /**
841
- * Microsoft Edge TTS is cloud-hosted and always ready immediately without local weight loading.
842
- */
843
- get isReady() {
844
- return !0;
845
- }
846
- /**
847
- * Initializes provider configuration. No weight downloading required.
848
- */
849
- async init(e) {
850
- e?.msedgeEndpoint && (this._config.msedgeEndpoint = e.msedgeEndpoint), e?.cacheAudio !== void 0 && (this._config.cacheAudio = e.cacheAudio);
851
- }
852
- /**
853
- * Synthesizes text into high-fidelity speech audio using Microsoft Edge neural voices.
854
- */
855
- async synthesize(e, t = {}) {
856
- const i = e.trim(), r = t.voice || L, s = Math.max(0.25, Math.min(4, t.speed ?? 1));
857
- if (!i)
858
- return new G({
859
- text: "",
860
- samples: new Float32Array(0),
861
- sampleRate: 24e3,
862
- voice: r,
863
- speed: s
864
- });
865
- if (this._config.cacheAudio) {
866
- const l = this._cache.get(i, r, s);
867
- if (l)
868
- return l;
869
- }
870
- const n = this._isBrowser ? await this._synthesizeBrowser(i, r, s, t) : await this._synthesizeNode(i, r, s, t);
871
- return this._config.cacheAudio && this._cache.set(i, r, s, n), n;
872
- }
873
- /**
874
- * Node.js execution: directly calls the msedge-tts package in-process.
875
- */
876
- async _synthesizeNode(e, t, i, r) {
877
- const { MsEdgeTTS: s, OUTPUT_FORMAT: n } = await import("msedge-tts");
878
- Be(s);
879
- const l = new s(), o = Math.round((i - 1) * 100), u = o >= 0 ? `+${o}%` : `${o}%`;
880
- await l.setMetadata(t, n.AUDIO_24KHZ_96KBITRATE_MONO_MP3, {
881
- wordBoundaryEnabled: !0,
882
- sentenceBoundaryEnabled: !0
883
- });
884
- const d = {
885
- rate: u
886
- };
887
- if (r.pitch !== void 0 && (d.pitch = r.pitch), r.volume !== void 0 && r.volume !== null) {
888
- if (typeof r.volume == "number")
889
- d.volume = r.volume <= 1 && r.volume > 0 ? Math.round(r.volume * 100) : Math.round(r.volume);
890
- else if (typeof r.volume == "string") {
891
- const A = parseFloat(r.volume);
892
- !isNaN(A) && !r.volume.includes("%") && !r.volume.startsWith("+") && !r.volume.startsWith("-") ? d.volume = A <= 1 && A > 0 ? Math.round(A * 100) : Math.round(A) : d.volume = r.volume;
893
- }
894
- }
895
- const { audioStream: c, metadataStream: f } = l.toStream(e, d), h = [], p = [];
896
- f && f.on("data", (A) => {
897
- try {
898
- const U = A.toString(), x = JSON.parse(U);
899
- if (Array.isArray(x?.Metadata)) {
900
- for (const M of x.Metadata)
901
- if (M.Type === "WordBoundary" && M.Data) {
902
- const F = (M.Data.Offset || 0) / 1e4, z = (M.Data.Duration || 0) / 1e4, R = M.Data.text?.Text || "";
903
- p.push({
904
- text: R,
905
- offsetMs: F,
906
- durationMs: z,
907
- boundaryType: "WordBoundary"
908
- });
909
- }
910
- }
911
- } catch {
912
- }
913
- }), await new Promise((A, U) => {
914
- c.on("data", (x) => {
915
- h.push(x);
916
- }), c.on("end", () => {
917
- A();
918
- }), c.on("error", (x) => {
919
- U(x);
920
- });
921
- });
922
- const b = Buffer.concat(h), m = b.buffer.slice(
923
- b.byteOffset,
924
- b.byteOffset + b.byteLength
925
- ), _ = H(new Uint8Array(m));
926
- return new G({
927
- text: e,
928
- rawBuffer: m,
929
- mimeType: "audio/mpeg",
930
- samples: new Float32Array(_.totalSamples),
931
- sampleRate: _.sampleRate,
932
- duration: _.durationSec,
933
- durationMs: _.durationMs,
934
- voice: t,
935
- speed: i,
936
- wordBoundaries: p.length > 0 ? p : void 0
937
- });
938
- }
939
- /**
940
- * Browser execution: invokes the Vite development server proxy endpoint.
941
- */
942
- async _synthesizeBrowser(e, t, i, r) {
943
- const s = `${this._config.msedgeEndpoint}/synthesize`, n = await fetch(s, {
944
- method: "POST",
945
- headers: {
946
- "Content-Type": "application/json"
947
- },
948
- body: JSON.stringify({
949
- text: e,
950
- voice: t,
951
- speed: i,
952
- pitch: r.pitch,
953
- volume: r.volume
954
- })
955
- });
956
- if (!n.ok)
957
- throw new Error(`[MsEdgeTTSProvider] Synthesis failed: HTTP ${n.status} ${n.statusText}`);
958
- const l = n.headers.get("X-Audio-Duration-Ms"), o = n.headers.get("X-Word-Boundaries");
959
- let u;
960
- if (o)
961
- try {
962
- u = JSON.parse(decodeURIComponent(o));
963
- } catch {
964
- }
965
- const d = await n.arrayBuffer();
966
- let c = new Float32Array(0), f = 24e3, h = l ? parseFloat(l) : 0;
967
- const p = window.AudioContext || window.webkitAudioContext;
968
- if (p) {
969
- const b = !!window.__retiam_audio_context, m = window.__retiam_audio_context || new p();
970
- try {
971
- const _ = await m.decodeAudioData(d.slice(0));
972
- c = _.getChannelData(0), f = _.sampleRate, h || (h = _.duration * 1e3);
973
- } catch (_) {
974
- console.warn("[MsEdgeTTSProvider] AudioContext decode deferred, using raw MP3 buffer:", _);
975
- } finally {
976
- !b && m && typeof m.close == "function" && m.close().catch(() => {
977
- });
978
- }
979
- }
980
- if (c.length === 0 || !h) {
981
- const b = H(new Uint8Array(d));
982
- f = b.sampleRate, h || (h = b.durationMs);
983
- }
984
- return new G({
985
- text: e,
986
- rawBuffer: d,
987
- mimeType: "audio/mpeg",
988
- samples: c,
989
- sampleRate: f,
990
- duration: h / 1e3,
991
- durationMs: h,
992
- voice: t,
993
- speed: i,
994
- wordBoundaries: u
995
- });
996
- }
997
- /**
998
- * Progressively streams synthesized speech sentence by sentence.
999
- */
1000
- async *stream(e, t = {}) {
1001
- const i = t.voice || L, r = Math.max(0.25, Math.min(4, t.speed ?? 1)), s = t.splitPattern || /([.!?;]+(?:\s+|$))/g, n = [], l = e.split(s);
1002
- for (let d = 0; d < l.length; d += 2) {
1003
- const c = (l[d] + (l[d + 1] || "")).trim();
1004
- c.length > 0 && n.push(c);
1005
- }
1006
- n.length === 0 && n.push(e.trim());
1007
- let o = 0, u = 0;
1008
- for (const d of n) {
1009
- const c = await this.synthesize(d, {
1010
- ...t,
1011
- voice: i,
1012
- speed: r
1013
- });
1014
- yield {
1015
- text: d,
1016
- phonemes: "",
1017
- audio: c,
1018
- chunkIndex: o++,
1019
- startTimeMs: u
1020
- }, u += c.durationMs;
1021
- }
1022
- }
1023
- /**
1024
- * Returns curated Microsoft Edge neural voices immediately.
1025
- */
1026
- getVoices() {
1027
- return W();
1028
- }
1029
- /**
1030
- * Fetches the complete live catalog of all 320+ Microsoft Edge neural voices.
1031
- */
1032
- async fetchVoices() {
1033
- if (this._isBrowser)
1034
- try {
1035
- const e = await fetch(`${this._config.msedgeEndpoint}/voices`);
1036
- if (e.ok) {
1037
- const t = await e.json();
1038
- if (Array.isArray(t))
1039
- return he(t), t;
1040
- }
1041
- } catch {
1042
- }
1043
- return ue();
1044
- }
1045
- /**
1046
- * Clears cached utterances.
1047
- */
1048
- clearCache() {
1049
- this._cache.clear();
1050
- }
1051
- /**
1052
- * Disposes resources and releases cache.
1053
- */
1054
- async dispose() {
1055
- this.clearCache();
1056
- for (const e of this._nodeClients)
1057
- try {
1058
- e.close();
1059
- } catch {
1060
- }
1061
- this._nodeClients = [];
1062
- }
1063
- }
1064
- class Z {
1065
- _provider;
1066
- _config;
1067
- constructor(e = {}, t) {
1068
- this._config = e, this._provider = t ?? new Me(e);
1069
- }
1070
- /** The active speech provider. */
1071
- get provider() {
1072
- return this._provider;
1073
- }
1074
- /** Whether the active provider is ready. */
1075
- get isReady() {
1076
- return this._provider.isReady;
1077
- }
1078
- /** Replaces the active provider instance. */
1079
- setProvider(e) {
1080
- this._provider = e;
1081
- }
1082
- /** Initializes the active provider. */
1083
- async init(e) {
1084
- e && (this._config = e), await this._provider.init(e ?? this._config);
1085
- }
1086
- /** Synthesizes text with the active provider. */
1087
- async synthesize(e, t = {}) {
1088
- return this._provider.synthesize(e, t);
1089
- }
1090
- /** Streams synthesized audio sentence by sentence. */
1091
- async *stream(e, t = {}) {
1092
- yield* this._provider.stream(e, t);
1093
- }
1094
- /** Synthesizes and plays speech through a Scene or AudioManager. */
1095
- async speak(e, t, i = {}) {
1096
- const r = await this.synthesize(t, i), s = await r.play(e, i);
1097
- return { audio: r, durationMs: s };
1098
- }
1099
- /** Returns voices exposed by the active provider. */
1100
- getVoices() {
1101
- return this._provider.getVoices();
1102
- }
1103
- /** Finds a voice by ID, display name, or partial match. */
1104
- findVoice(e) {
1105
- const t = e.toLowerCase();
1106
- return this._provider.getVoices().find(
1107
- (i) => i.id.toLowerCase() === t || i.name.toLowerCase() === t || i.id.toLowerCase().includes(t) || i.name.toLowerCase().includes(t)
1108
- ) ?? ce(e);
1109
- }
1110
- /** Releases provider resources and cached audio. */
1111
- async dispose() {
1112
- await this._provider.dispose();
1113
- }
1114
- }
1115
- let O = null;
1116
- function E() {
1117
- return O || (O = new Z()), O;
1118
- }
1119
- class j {
1120
- /** Replaces the global client configuration and restores the built-in provider. */
1121
- static configure(e) {
1122
- O = new Z(e);
1123
- }
1124
- /** Initializes the global speech provider. */
1125
- static async init(e) {
1126
- e && this.configure(e), await E().init(e);
1127
- }
1128
- /** Replaces the global provider instance. */
1129
- static setProvider(e) {
1130
- E().setProvider(e);
1131
- }
1132
- /** Active global provider. */
1133
- static get provider() {
1134
- return E().provider;
1135
- }
1136
- /** Synthesizes text to speech. */
1137
- static async synthesize(e, t) {
1138
- return E().synthesize(e, t);
1139
- }
1140
- /** Explicit alias for Microsoft Edge neural synthesis. */
1141
- static async msedge(e, t) {
1142
- return E().synthesize(e, t);
1143
- }
1144
- /** Streams synthesized speech sentence by sentence. */
1145
- static async *stream(e, t) {
1146
- yield* E().stream(e, t);
1147
- }
1148
- /** Synthesizes and plays speech through a Scene or AudioManager. */
1149
- static async speak(e, t, i) {
1150
- return E().speak(e, t, i);
1151
- }
1152
- /** Returns the active provider's voices. */
1153
- static getVoices() {
1154
- return E().getVoices();
1155
- }
1156
- /** Finds a Microsoft Edge neural voice. */
1157
- static findVoice(e) {
1158
- return E().findVoice(e);
1159
- }
1160
- /** Recommended default voice. */
1161
- static DEFAULT_VOICE = L;
1162
- /** Explicit provider-specific alias for the recommended default voice. */
1163
- static DEFAULT_MSEDGE_VOICE = L;
1164
- }
1165
- class Fe extends te {
6
+ class F extends x {
1166
7
  voice;
1167
8
  speed;
9
+ elevenlabs;
1168
10
  animateSpeaking;
1169
11
  pulseAmplitude;
1170
12
  pulseFrequency;
@@ -1174,13 +16,13 @@ class Fe extends te {
1174
16
  _baseScaleX = 1;
1175
17
  _baseScaleY = 1;
1176
18
  _baseScaleZ = 1;
1177
- constructor(e = {}) {
1178
- super(), this.voice = e.voice ?? "en-US-JennyNeural", this.speed = e.speed ?? 1, this.animateSpeaking = e.animateSpeaking !== !1, this.pulseAmplitude = e.pulseAmplitude ?? 0.05, this.pulseFrequency = e.pulseFrequency ?? 4, this.onSpeechStart = e.onSpeechStart, this.onSpeechEnd = e.onSpeechEnd;
19
+ constructor(t = {}) {
20
+ super(), this.voice = t.voice, this.speed = t.speed, this.elevenlabs = t.elevenlabs, this.animateSpeaking = t.animateSpeaking !== !1, this.pulseAmplitude = t.pulseAmplitude ?? 0.05, this.pulseFrequency = t.pulseFrequency ?? 4, this.onSpeechStart = t.onSpeechStart, this.onSpeechEnd = t.onSpeechEnd;
1179
21
  }
1180
22
  onInit() {
1181
23
  if (this.entity?.transform) {
1182
- const e = this.entity.transform.getScale();
1183
- this._baseScaleX = e.x, this._baseScaleY = e.y, this._baseScaleZ = e.z;
24
+ const t = this.entity.transform.getScale();
25
+ this._baseScaleX = t.x, this._baseScaleY = t.y, this._baseScaleZ = t.z;
1184
26
  }
1185
27
  }
1186
28
  /**
@@ -1196,23 +38,32 @@ class Fe extends te {
1196
38
  * @param overrides Voice and playback option overrides.
1197
39
  * @returns Synthesized [[TTSAudioResult]] and total speech duration in milliseconds.
1198
40
  */
1199
- async speak(e, t = {}) {
1200
- const i = this.entity?.scene, r = t.voice ?? this.voice, s = t.speed ?? this.speed;
41
+ async speak(t, i = {}) {
42
+ const e = this.entity?.scene, s = i.voice ?? this.voice, c = i.speed ?? this.speed;
1201
43
  if (this.entity?.transform) {
1202
- const o = this.entity.transform.getScale();
1203
- this._baseScaleX = o.x, this._baseScaleY = o.y, this._baseScaleZ = o.z;
44
+ const h = this.entity.transform.getScale();
45
+ this._baseScaleX = h.x, this._baseScaleY = h.y, this._baseScaleZ = h.z;
1204
46
  }
1205
- const n = await j.synthesize(e, { voice: r, speed: s, ...t });
1206
- this._isSpeaking = !0, this.onSpeechStart?.(e, n);
1207
- const l = await n.play(i, t);
1208
- return this._isSpeaking = !1, this._restoreBaseScale(), this.onSpeechEnd?.(e), { audio: n, durationMs: l };
47
+ const l = await p.synthesize(t, {
48
+ voice: s,
49
+ speed: c,
50
+ ...i,
51
+ elevenlabs: {
52
+ ...this.elevenlabs,
53
+ ...i.elevenlabs,
54
+ voiceSettings: { ...this.elevenlabs?.voiceSettings, ...i.elevenlabs?.voiceSettings }
55
+ }
56
+ });
57
+ this._isSpeaking = !0, this.onSpeechStart?.(t, l);
58
+ const o = await l.play(e, i);
59
+ return this._isSpeaking = !1, this._restoreBaseScale(), this.onSpeechEnd?.(t), { audio: l, durationMs: o };
1209
60
  }
1210
- update(e, t) {
61
+ update(t, i) {
1211
62
  if (!(!this.enabled || !this.entity?.transform) && this._isSpeaking && this.animateSpeaking) {
1212
- const i = e / 1e3 * this.pulseFrequency * Math.PI * 2, r = 1 + Math.sin(i) * this.pulseAmplitude;
63
+ const e = t / 1e3 * this.pulseFrequency * Math.PI * 2, s = 1 + Math.sin(e) * this.pulseAmplitude;
1213
64
  this.entity.transform.setScale([
1214
- this._baseScaleX * r,
1215
- this._baseScaleY * r,
65
+ this._baseScaleX * s,
66
+ this._baseScaleY * s,
1216
67
  this._baseScaleZ
1217
68
  ]), this.entity.version++;
1218
69
  }
@@ -1228,14 +79,14 @@ class Fe extends te {
1228
79
  this._isSpeaking = !1, this._restoreBaseScale();
1229
80
  }
1230
81
  }
1231
- const Ne = {
82
+ const v = {
1232
83
  obsidian: {
1233
84
  fontSize: 0.38,
1234
- textColor: w("#F8FAFC"),
85
+ textColor: a("#F8FAFC"),
1235
86
  textOpacity: 1,
1236
- backdropColor: w("#060913"),
87
+ backdropColor: a("#060913"),
1237
88
  backdropOpacity: 0.85,
1238
- strokeColor: new C(0.22, 0.74, 0.97, 0.35),
89
+ strokeColor: new d(0.22, 0.74, 0.97, 0.35),
1239
90
  strokeWidth: 0.02,
1240
91
  cornerRadius: 0.22,
1241
92
  pillShape: !1,
@@ -1246,11 +97,11 @@ const Ne = {
1246
97
  },
1247
98
  pill: {
1248
99
  fontSize: 0.38,
1249
- textColor: w("#FFFFFF"),
100
+ textColor: a("#FFFFFF"),
1250
101
  textOpacity: 1,
1251
- backdropColor: w("#080D1A"),
102
+ backdropColor: a("#080D1A"),
1252
103
  backdropOpacity: 0.88,
1253
- strokeColor: new C(0.38, 0.52, 0.75, 0.4),
104
+ strokeColor: new d(0.38, 0.52, 0.75, 0.4),
1254
105
  strokeWidth: 0.02,
1255
106
  pillShape: !0,
1256
107
  padding: [0.7, 0.28],
@@ -1260,11 +111,11 @@ const Ne = {
1260
111
  },
1261
112
  minimal: {
1262
113
  fontSize: 0.38,
1263
- textColor: w("#F1F5F9"),
114
+ textColor: a("#F1F5F9"),
1264
115
  textOpacity: 1,
1265
- backdropColor: w("#090D16"),
116
+ backdropColor: a("#090D16"),
1266
117
  backdropOpacity: 0.8,
1267
- strokeColor: C.NONE,
118
+ strokeColor: d.NONE,
1268
119
  strokeWidth: 0,
1269
120
  cornerRadius: 0.16,
1270
121
  pillShape: !1,
@@ -1275,11 +126,11 @@ const Ne = {
1275
126
  },
1276
127
  broadcast: {
1277
128
  fontSize: 0.38,
1278
- textColor: w("#FFFFFF"),
129
+ textColor: a("#FFFFFF"),
1279
130
  textOpacity: 1,
1280
- backdropColor: w("#000000"),
131
+ backdropColor: a("#000000"),
1281
132
  backdropOpacity: 0.92,
1282
- strokeColor: C.NONE,
133
+ strokeColor: d.NONE,
1283
134
  strokeWidth: 0,
1284
135
  cornerRadius: 0.08,
1285
136
  pillShape: !1,
@@ -1290,11 +141,11 @@ const Ne = {
1290
141
  },
1291
142
  neon: {
1292
143
  fontSize: 0.38,
1293
- textColor: w("#E0F2FE"),
144
+ textColor: a("#E0F2FE"),
1294
145
  textOpacity: 1,
1295
- backdropColor: w("#030712"),
146
+ backdropColor: a("#030712"),
1296
147
  backdropOpacity: 0.92,
1297
- strokeColor: w("#00F0FF"),
148
+ strokeColor: a("#00F0FF"),
1298
149
  strokeWidth: 0.025,
1299
150
  cornerRadius: 0.24,
1300
151
  pillShape: !1,
@@ -1305,11 +156,11 @@ const Ne = {
1305
156
  },
1306
157
  glass: {
1307
158
  fontSize: 0.38,
1308
- textColor: w("#FFFFFF"),
159
+ textColor: a("#FFFFFF"),
1309
160
  textOpacity: 1,
1310
- backdropColor: new C(0.05, 0.09, 0.15, 0.65),
161
+ backdropColor: new d(0.05, 0.09, 0.15, 0.65),
1311
162
  backdropOpacity: 0.65,
1312
- strokeColor: new C(1, 1, 1, 0.25),
163
+ strokeColor: new d(1, 1, 1, 0.25),
1313
164
  strokeWidth: 0.015,
1314
165
  cornerRadius: 0.25,
1315
166
  pillShape: !1,
@@ -1320,11 +171,11 @@ const Ne = {
1320
171
  },
1321
172
  light: {
1322
173
  fontSize: 0.38,
1323
- textColor: w("#0F172A"),
174
+ textColor: a("#0F172A"),
1324
175
  textOpacity: 1,
1325
- backdropColor: w("#F8FAFC"),
176
+ backdropColor: a("#F8FAFC"),
1326
177
  backdropOpacity: 0.92,
1327
- strokeColor: new C(0.39, 0.45, 0.55, 0.25),
178
+ strokeColor: new d(0.39, 0.45, 0.55, 0.25),
1328
179
  strokeWidth: 0.015,
1329
180
  cornerRadius: 0.2,
1330
181
  pillShape: !1,
@@ -1334,18 +185,18 @@ const Ne = {
1334
185
  showBackdrop: !0
1335
186
  }
1336
187
  };
1337
- function k(a, e) {
1338
- return a ? typeof a == "string" ? w(a) : a : e;
188
+ function n(r, t) {
189
+ return r ? typeof r == "string" ? a(r) : r : t;
1339
190
  }
1340
- class Re extends ie {
191
+ class R extends b {
1341
192
  _textEntity;
1342
193
  _backdrop = null;
1343
194
  _fontSize = 0.38;
1344
- _textColor = w("#F8FAFC");
195
+ _textColor = a("#F8FAFC");
1345
196
  _textOpacity = 1;
1346
- _backdropColor = w("#060913");
197
+ _backdropColor = a("#060913");
1347
198
  _backdropOpacity = 0.85;
1348
- _strokeColor = new C(0.22, 0.74, 0.97, 0.35);
199
+ _strokeColor = new d(0.22, 0.74, 0.97, 0.35);
1349
200
  _strokeWidth = 0.02;
1350
201
  _showBorder = !0;
1351
202
  _cornerRadius = 0.22;
@@ -1356,16 +207,16 @@ class Re extends ie {
1356
207
  _maxLineChars = 65;
1357
208
  _lineHeight = 1.35;
1358
209
  _clearTimeout = null;
1359
- constructor(e = {}) {
210
+ constructor(t = {}) {
1360
211
  super();
1361
- const t = e.zIndex ?? 999;
1362
- this._textEntity = new ae("", {
212
+ const i = t.zIndex ?? 999;
213
+ this._textEntity = new m("", {
1363
214
  fontSize: this._fontSize,
1364
215
  fill: this._textColor,
1365
- anchor: re
1366
- }), this._textEntity.paint.z_index = t + 1, this._backdrop = new se(2, 1, { cornerRadius: this._cornerRadius }), this._backdrop.paint.fill = this._backdropColor, this._backdrop.paint.stroke = this._strokeColor, this._backdrop.paint.strokeWidth = this._strokeWidth, this._backdrop.paint.z_index = t, this._backdrop.paint.opacity = 0, this.add(this._backdrop), this.add(this._textEntity), this.paint.z_index = t, this.applyOptions(e);
1367
- const i = e.bottomOffset ?? -5;
1368
- this.transform.position.set([0, i, 0]);
216
+ anchor: S
217
+ }), this._textEntity.paint.z_index = i + 1, this._backdrop = new g(2, 1, { cornerRadius: this._cornerRadius }), this._backdrop.paint.fill = this._backdropColor, this._backdrop.paint.stroke = this._strokeColor, this._backdrop.paint.strokeWidth = this._strokeWidth, this._backdrop.paint.z_index = i, this._backdrop.paint.opacity = 0, this.add(this._backdrop), this.add(this._textEntity), this.paint.z_index = i, this.applyOptions(t);
218
+ const e = t.bottomOffset ?? -5;
219
+ this.transform.position.set([0, e, 0]);
1369
220
  }
1370
221
  /** Access the underlying SDFText entity for advanced low-level styling */
1371
222
  get textEntity() {
@@ -1378,128 +229,128 @@ class Re extends ie {
1378
229
  /**
1379
230
  * Applies a complete configuration options object or partial updates.
1380
231
  */
1381
- applyOptions(e = {}) {
1382
- const i = { ...e.preset ? Ne[e.preset] : {}, ...e };
1383
- return i.fontSize !== void 0 && (this._fontSize = i.fontSize, this._textEntity.fontSize = i.fontSize), i.textColor !== void 0 && (this._textColor = k(i.textColor, this._textColor), this._textEntity.paint.fill = this._textColor), i.textOpacity !== void 0 && (this._textOpacity = i.textOpacity), i.backdropColor !== void 0 && (this._backdropColor = k(i.backdropColor, this._backdropColor)), i.backdropOpacity !== void 0 && (this._backdropOpacity = i.backdropOpacity), i.strokeColor !== void 0 && (this._strokeColor = k(i.strokeColor, this._strokeColor)), i.strokeWidth !== void 0 && (this._strokeWidth = i.strokeWidth), i.showBorder !== void 0 && (this._showBorder = i.showBorder), i.cornerRadius !== void 0 && (this._cornerRadius = i.cornerRadius), i.pillShape !== void 0 && (this._pillShape = i.pillShape), i.showBackdrop !== void 0 && (this._showBackdrop = i.showBackdrop), i.padding !== void 0 && (typeof i.padding == "number" ? this._padding = [i.padding, i.padding] : Array.isArray(i.padding) && (this._padding = [i.padding[0], i.padding[1]])), i.maxWidth !== void 0 && (this._maxWidth = i.maxWidth), i.maxLineChars !== void 0 && (this._maxLineChars = Math.max(15, i.maxLineChars)), i.lineHeight !== void 0 && (this._lineHeight = i.lineHeight), i.bottomOffset !== void 0 && this.setBottomOffset(i.bottomOffset), this._textEntity.text && this._updateBackdropDimensions(this._textEntity.text), this.version++, this;
232
+ applyOptions(t = {}) {
233
+ const e = { ...t.preset ? v[t.preset] : {}, ...t };
234
+ return e.fontSize !== void 0 && (this._fontSize = e.fontSize, this._textEntity.fontSize = e.fontSize), e.textColor !== void 0 && (this._textColor = n(e.textColor, this._textColor), this._textEntity.paint.fill = this._textColor), e.textOpacity !== void 0 && (this._textOpacity = e.textOpacity), e.backdropColor !== void 0 && (this._backdropColor = n(e.backdropColor, this._backdropColor)), e.backdropOpacity !== void 0 && (this._backdropOpacity = e.backdropOpacity), e.strokeColor !== void 0 && (this._strokeColor = n(e.strokeColor, this._strokeColor)), e.strokeWidth !== void 0 && (this._strokeWidth = e.strokeWidth), e.showBorder !== void 0 && (this._showBorder = e.showBorder), e.cornerRadius !== void 0 && (this._cornerRadius = e.cornerRadius), e.pillShape !== void 0 && (this._pillShape = e.pillShape), e.showBackdrop !== void 0 && (this._showBackdrop = e.showBackdrop), e.padding !== void 0 && (typeof e.padding == "number" ? this._padding = [e.padding, e.padding] : Array.isArray(e.padding) && (this._padding = [e.padding[0], e.padding[1]])), e.maxWidth !== void 0 && (this._maxWidth = e.maxWidth), e.maxLineChars !== void 0 && (this._maxLineChars = Math.max(15, e.maxLineChars)), e.lineHeight !== void 0 && (this._lineHeight = e.lineHeight), e.bottomOffset !== void 0 && this.setBottomOffset(e.bottomOffset), this._textEntity.text && this._updateBackdropDimensions(this._textEntity.text), this.version++, this;
1384
235
  }
1385
236
  /** Set an aesthetic preset theme */
1386
- setPreset(e) {
1387
- return this.applyOptions({ preset: e });
237
+ setPreset(t) {
238
+ return this.applyOptions({ preset: t });
1388
239
  }
1389
240
  /** Set the corner radius of the backdrop pill without distortion */
1390
- setCornerRadius(e) {
1391
- return this._cornerRadius = e, this._pillShape = !1, this._textEntity.text && this._updateBackdropDimensions(this._textEntity.text), this;
241
+ setCornerRadius(t) {
242
+ return this._cornerRadius = t, this._pillShape = !1, this._textEntity.text && this._updateBackdropDimensions(this._textEntity.text), this;
1392
243
  }
1393
244
  /** Enable or disable stadium pill shape (semicircular ends where cornerRadius = height / 2) */
1394
- setPillShape(e = !0) {
1395
- return this._pillShape = e, this._textEntity.text && this._updateBackdropDimensions(this._textEntity.text), this;
245
+ setPillShape(t = !0) {
246
+ return this._pillShape = t, this._textEntity.text && this._updateBackdropDimensions(this._textEntity.text), this;
1396
247
  }
1397
248
  /** Set the inner horizontal and vertical padding around the subtitle text */
1398
- setPadding(e, t = e) {
1399
- return this._padding = [e, t], this._textEntity.text && this._updateBackdropDimensions(this._textEntity.text), this;
249
+ setPadding(t, i = t) {
250
+ return this._padding = [t, i], this._textEntity.text && this._updateBackdropDimensions(this._textEntity.text), this;
1400
251
  }
1401
252
  /** Configure backdrop fill color and opacity */
1402
- setBackdropColor(e, t) {
1403
- return this._backdropColor = k(e, this._backdropColor), t !== void 0 && (this._backdropOpacity = Math.max(0, Math.min(1, t))), this._backdrop && (this._backdrop.paint.fill = this._backdropColor, this._textEntity.text && (this._backdrop.paint.opacity = this._backdropOpacity), this._backdrop.invalidate()), this;
253
+ setBackdropColor(t, i) {
254
+ return this._backdropColor = n(t, this._backdropColor), i !== void 0 && (this._backdropOpacity = Math.max(0, Math.min(1, i))), this._backdrop && (this._backdrop.paint.fill = this._backdropColor, this._textEntity.text && (this._backdrop.paint.opacity = this._backdropOpacity), this._backdrop.invalidate()), this;
1404
255
  }
1405
- setStroke(e, t) {
1406
- return typeof e == "number" ? (this._strokeWidth = e, this._showBorder = e > 0, t !== void 0 && (typeof t == "string" || t instanceof C) && (this._strokeColor = k(t, this._strokeColor))) : (this._strokeColor = k(e, this._strokeColor), typeof t == "number" && (this._strokeWidth = t, this._showBorder = t > 0)), this._backdrop && (this._backdrop.paint.stroke = this._strokeColor, this._backdrop.paint.strokeWidth = this._strokeWidth, this._backdrop.invalidate()), this;
256
+ setStroke(t, i) {
257
+ return typeof t == "number" ? (this._strokeWidth = t, this._showBorder = t > 0, i !== void 0 && (typeof i == "string" || i instanceof d) && (this._strokeColor = n(i, this._strokeColor))) : (this._strokeColor = n(t, this._strokeColor), typeof i == "number" && (this._strokeWidth = i, this._showBorder = i > 0)), this._backdrop && (this._backdrop.paint.stroke = this._strokeColor, this._backdrop.paint.strokeWidth = this._strokeWidth, this._backdrop.invalidate()), this;
1407
258
  }
1408
259
  /** Set backdrop border stroke color */
1409
- setStrokeColor(e) {
1410
- return this._strokeColor = k(e, this._strokeColor), this._backdrop && (this._backdrop.paint.stroke = this._strokeColor, this._backdrop.invalidate()), this;
260
+ setStrokeColor(t) {
261
+ return this._strokeColor = n(t, this._strokeColor), this._backdrop && (this._backdrop.paint.stroke = this._strokeColor, this._backdrop.invalidate()), this;
1411
262
  }
1412
263
  /** Set backdrop border stroke width */
1413
- setStrokeWidth(e) {
1414
- return this._strokeWidth = e, this._showBorder = e > 0, this._backdrop && (this._backdrop.paint.strokeWidth = this._strokeWidth, this._backdrop.invalidate()), this;
264
+ setStrokeWidth(t) {
265
+ return this._strokeWidth = t, this._showBorder = t > 0, this._backdrop && (this._backdrop.paint.strokeWidth = this._strokeWidth, this._backdrop.invalidate()), this;
1415
266
  }
1416
267
  /** Configure subtitle typography (font color and world unit font size) */
1417
- setTextColor(e, t = 1) {
1418
- return this._textColor = k(e, this._textColor), this._textOpacity = t, this._textEntity.paint.fill = this._textColor, this._textEntity.paint.opacity = t, this.version++, this;
268
+ setTextColor(t, i = 1) {
269
+ return this._textColor = n(t, this._textColor), this._textOpacity = i, this._textEntity.paint.fill = this._textColor, this._textEntity.paint.opacity = i, this.version++, this;
1419
270
  }
1420
271
  /** Set subtitle font size in world units */
1421
- setFontSize(e) {
1422
- return e > 0 && (this._fontSize = e, this._textEntity.fontSize = e, this._textEntity.text && this._updateBackdropDimensions(this._textEntity.text)), this;
272
+ setFontSize(t) {
273
+ return t > 0 && (this._fontSize = t, this._textEntity.fontSize = t, this._textEntity.text && this._updateBackdropDimensions(this._textEntity.text)), this;
1423
274
  }
1424
275
  /** Configure backdrop visual styling (colors, stroke border, corner radius) */
1425
- setBackdropStyle(e, t, i, r) {
1426
- return this._backdropColor = k(e, this._backdropColor), t !== void 0 && (this._strokeColor = k(t, this._strokeColor), this._showBorder = !0), i !== void 0 && (this._strokeWidth = i, this._showBorder = i > 0), r !== void 0 && (this._cornerRadius = r, this._pillShape = !1), this._textEntity.text && this._updateBackdropDimensions(this._textEntity.text), this.version++, this;
276
+ setBackdropStyle(t, i, e, s) {
277
+ return this._backdropColor = n(t, this._backdropColor), i !== void 0 && (this._strokeColor = n(i, this._strokeColor), this._showBorder = !0), e !== void 0 && (this._strokeWidth = e, this._showBorder = e > 0), s !== void 0 && (this._cornerRadius = s, this._pillShape = !1), this._textEntity.text && this._updateBackdropDimensions(this._textEntity.text), this.version++, this;
1427
278
  }
1428
279
  /** Configure subtitle typography */
1429
- setTextStyle(e, t) {
1430
- return this._textColor = k(e, this._textColor), this._textEntity.paint.fill = this._textColor, t !== void 0 && t > 0 && (this._fontSize = t, this._textEntity.fontSize = t), this._textEntity.text && this._updateBackdropDimensions(this._textEntity.text), this.version++, this;
280
+ setTextStyle(t, i) {
281
+ return this._textColor = n(t, this._textColor), this._textEntity.paint.fill = this._textColor, i !== void 0 && i > 0 && (this._fontSize = i, this._textEntity.fontSize = i), this._textEntity.text && this._updateBackdropDimensions(this._textEntity.text), this.version++, this;
1431
282
  }
1432
283
  /** Set maximum characters per line before word-wrapping occurs */
1433
- setMaxLineChars(e) {
1434
- return this._maxLineChars = Math.max(15, e), this;
284
+ setMaxLineChars(t) {
285
+ return this._maxLineChars = Math.max(15, t), this;
1435
286
  }
1436
287
  /** Set maximum bounding box width in world units */
1437
- setMaxWidth(e) {
1438
- return this._maxWidth = e, this;
288
+ setMaxWidth(t) {
289
+ return this._maxWidth = t, this;
1439
290
  }
1440
291
  /** Positions the subtitles at the bottom center with a custom Y world coordinate */
1441
- setBottomOffset(e) {
1442
- return this.transform.position.set([0, e, 0]), this;
292
+ setBottomOffset(t) {
293
+ return this.transform.position.set([0, t, 0]), this;
1443
294
  }
1444
295
  /**
1445
296
  * Computes safe corner radius: clamped so circular arcs never self-intersect,
1446
297
  * or computes stadium capsule radius when pillShape is enabled.
1447
298
  */
1448
- _getEffectiveCornerRadius(e, t) {
1449
- const i = Math.min(e, t) / 2;
1450
- return this._pillShape ? i : typeof this._cornerRadius == "number" ? Math.max(0, Math.min(this._cornerRadius, i)) : Array.isArray(this._cornerRadius) ? [
1451
- Math.max(0, Math.min(this._cornerRadius[0], i)),
1452
- Math.max(0, Math.min(this._cornerRadius[1], i)),
1453
- Math.max(0, Math.min(this._cornerRadius[2], i)),
1454
- Math.max(0, Math.min(this._cornerRadius[3], i))
1455
- ] : Math.min(0.22, i);
299
+ _getEffectiveCornerRadius(t, i) {
300
+ const e = Math.min(t, i) / 2;
301
+ return this._pillShape ? e : typeof this._cornerRadius == "number" ? Math.max(0, Math.min(this._cornerRadius, e)) : Array.isArray(this._cornerRadius) ? [
302
+ Math.max(0, Math.min(this._cornerRadius[0], e)),
303
+ Math.max(0, Math.min(this._cornerRadius[1], e)),
304
+ Math.max(0, Math.min(this._cornerRadius[2], e)),
305
+ Math.max(0, Math.min(this._cornerRadius[3], e))
306
+ ] : Math.min(0.22, e);
1456
307
  }
1457
308
  /**
1458
309
  * Internal geometry reflow: updates backdrop width & height using genuine Rect
1459
310
  * geometry regeneration (Rect.sync()) to preserve pristine, circular corner arcs.
1460
311
  */
1461
- _updateBackdropDimensions(e) {
312
+ _updateBackdropDimensions(t) {
1462
313
  if (!this._backdrop) return;
1463
- if (!this._showBackdrop || !e || e.trim().length === 0) {
314
+ if (!this._showBackdrop || !t || t.trim().length === 0) {
1464
315
  this._backdrop.paint.opacity = 0, this._backdrop.invalidate();
1465
316
  return;
1466
317
  }
1467
- const t = this._textEntity.getGroupLocalAABB(), i = t.isEmpty() ? 0 : t.size.x, r = t.isEmpty() ? 0 : t.size.y, [s, n] = this._padding;
1468
- let l = i, o = r;
1469
- if (l <= 0.1 || o <= 0.1) {
1470
- const f = e.split(`
318
+ const i = this._textEntity.getGroupLocalAABB(), e = i.isEmpty() ? 0 : i.size.x, s = i.isEmpty() ? 0 : i.size.y, [c, l] = this._padding;
319
+ let o = e, h = s;
320
+ if (o <= 0.1 || h <= 0.1) {
321
+ const f = t.split(`
1471
322
  `);
1472
- l = Math.max(...f.map((p) => p.length)) * (this._fontSize * 0.44), o = f.length * (this._fontSize * this._lineHeight);
323
+ o = Math.max(...f.map((y) => y.length)) * (this._fontSize * 0.44), h = f.length * (this._fontSize * this._lineHeight);
1473
324
  }
1474
- const u = Math.max(2.4, Math.min(this._maxWidth, l + s * 2)), d = o + n * 2, c = this._getEffectiveCornerRadius(u, d);
1475
- this._backdrop.transform.setScale([1, 1, 1]), this._backdrop.width = u, this._backdrop.height = d, this._backdrop.cornerRadius = c, this._backdrop.paint.fill = this._backdropColor, this._backdrop.paint.stroke = this._showBorder ? this._strokeColor : C.NONE, this._backdrop.paint.strokeWidth = this._showBorder ? this._strokeWidth : 0, this._backdrop.paint.opacity = this._backdropOpacity, this._backdrop.invalidate();
325
+ const _ = Math.max(2.4, Math.min(this._maxWidth, o + c * 2)), u = h + l * 2, k = this._getEffectiveCornerRadius(_, u);
326
+ this._backdrop.transform.setScale([1, 1, 1]), this._backdrop.width = _, this._backdrop.height = u, this._backdrop.cornerRadius = k, this._backdrop.paint.fill = this._backdropColor, this._backdrop.paint.stroke = this._showBorder ? this._strokeColor : d.NONE, this._backdrop.paint.strokeWidth = this._showBorder ? this._strokeWidth : 0, this._backdrop.paint.opacity = this._backdropOpacity, this._backdrop.invalidate();
1476
327
  }
1477
328
  /**
1478
329
  * Displays a subtitle sentence, formatting lines with word-wrapping and
1479
330
  * reflowing the backdrop pill with true circular corner arcs.
1480
331
  */
1481
- showText(e, t) {
332
+ showText(t, i) {
1482
333
  this._clearTimeout && (clearTimeout(this._clearTimeout), this._clearTimeout = null);
1483
- const i = e.trim();
1484
- let r = i;
1485
- if (i.length > this._maxLineChars) {
1486
- const s = i.split(" "), n = [];
1487
- let l = "";
1488
- for (const o of s)
1489
- (l + " " + o).trim().length > this._maxLineChars ? (l && n.push(l), l = o) : l = l ? l + " " + o : o;
1490
- l && n.push(l), r = n.join(`
334
+ const e = t.trim();
335
+ let s = e;
336
+ if (e.length > this._maxLineChars) {
337
+ const c = e.split(" "), l = [];
338
+ let o = "";
339
+ for (const h of c)
340
+ (o + " " + h).trim().length > this._maxLineChars ? (o && l.push(o), o = h) : o = o ? o + " " + h : h;
341
+ o && l.push(o), s = l.join(`
1491
342
  `);
1492
343
  }
1493
- return this._textEntity.text = r, this._textEntity.paint.opacity = this._textOpacity, this._updateBackdropDimensions(r), this.version++, t && t > 0 && (this._clearTimeout = setTimeout(() => {
344
+ return this._textEntity.text = s, this._textEntity.paint.opacity = this._textOpacity, this._updateBackdropDimensions(s), this.version++, i && i > 0 && (this._clearTimeout = setTimeout(() => {
1494
345
  this.clear();
1495
- }, t)), this;
346
+ }, i)), this;
1496
347
  }
1497
348
  /**
1498
349
  * Synchronizes this subtitle banner with a [[TTSAudioResult]], displaying the spoken text
1499
350
  * for the duration of the utterance and clearing on finish.
1500
351
  */
1501
- syncWithAudio(e) {
1502
- return this.showText(e.text, e.durationMs);
352
+ syncWithAudio(t) {
353
+ return this.showText(t.text, t.durationMs);
1503
354
  }
1504
355
  /**
1505
356
  * Immediately clears and hides the subtitle banner.
@@ -1508,7 +359,7 @@ class Re extends ie {
1508
359
  return this._clearTimeout && (clearTimeout(this._clearTimeout), this._clearTimeout = null), this._textEntity.text = "", this._textEntity.paint.opacity = 0, this._backdrop && (this._backdrop.paint.opacity = 0, this._backdrop.invalidate()), this.version++, this;
1509
360
  }
1510
361
  }
1511
- class De extends ne {
362
+ class T extends C {
1512
363
  text;
1513
364
  voice;
1514
365
  speed;
@@ -1519,40 +370,56 @@ class De extends ne {
1519
370
  duckingRatio;
1520
371
  volume;
1521
372
  _audioResult = null;
373
+ _audioTask = null;
374
+ _synthesisOptions;
1522
375
  _isStarted = !1;
1523
376
  _baseScale = [1, 1, 1];
1524
- constructor(e, t = {}) {
1525
- super(t), this.text = e, this.voice = t.voice ?? "en-US-JennyNeural", this.speed = t.speed ?? 1, this.pitch = t.pitch, this.subtitles = t.subtitles, this.targetEntity = t.entity, this.pulseAmplitude = t.pulseAmplitude ?? 0.05, this.duckingRatio = t.duckingRatio ?? 0.25, this.volume = t.volume ?? 1;
1526
- const i = e.trim().split(/\s+/).length;
1527
- this.duration = Math.max(1e3, i * 420 * (1 / this.speed));
377
+ constructor(t, i = {}) {
378
+ super(i), this.text = t, this.voice = i.voice, this.speed = i.speed ?? 1, this.pitch = i.pitch, this.subtitles = i.subtitles, this.targetEntity = i.entity, this.pulseAmplitude = i.pulseAmplitude ?? 0.05, this.duckingRatio = i.duckingRatio ?? 0.25, this.volume = i.volume ?? 1, this._synthesisOptions = {
379
+ voice: i.voice,
380
+ speed: i.speed,
381
+ pitch: i.pitch,
382
+ volume: i.volume,
383
+ displayText: i.displayText,
384
+ elevenlabs: i.elevenlabs
385
+ };
386
+ const e = t.trim().split(/\s+/).length;
387
+ this.duration = Math.max(1e3, e * 420 * (1 / this.speed));
1528
388
  }
1529
389
  /**
1530
390
  * Prepares animation state.
1531
391
  */
1532
392
  prepare() {
1533
393
  if (this.targetEntity?.transform) {
1534
- const e = this.targetEntity.transform.getScale();
1535
- this._baseScale = [e.x, e.y, e.z];
394
+ const t = this.targetEntity.transform.getScale();
395
+ this._baseScale = [t.x, t.y, t.z];
1536
396
  }
1537
397
  }
1538
398
  /**
1539
399
  * Synthesizes audio ahead of time and locks exact duration.
1540
400
  */
1541
401
  async prepareAudio() {
1542
- return this._audioResult || (this._audioResult = await j.synthesize(this.text, {
1543
- voice: this.voice,
1544
- speed: this.speed,
1545
- pitch: this.pitch,
1546
- volume: this.volume
1547
- }), this.duration = this._audioResult.durationMs), this._audioResult;
402
+ if (!this._audioResult) {
403
+ this._audioTask ??= p.synthesize(this.text, this._synthesisOptions);
404
+ try {
405
+ this._audioResult = await this._audioTask, this.duration = this._audioResult.durationMs;
406
+ } finally {
407
+ this._audioTask = null;
408
+ }
409
+ }
410
+ return this._audioResult;
1548
411
  }
1549
- update(e) {
412
+ update(t) {
1550
413
  if (this.paused) return !1;
1551
- if (this._isStarted || (this._isStarted = !0, this._beginPlayback()), this.elapsed += e, this.targetEntity?.transform) {
1552
- const t = this.elapsed / 1e3 * 4 * Math.PI * 2, i = 1 + Math.sin(t) * this.pulseAmplitude;
414
+ if (this.delay > 0) {
415
+ if (this.delay -= t, this.delay > 0) return !1;
416
+ t = -this.delay, this.delay = 0;
417
+ }
418
+ if (this._isStarted || (this._isStarted = !0, this._beginPlayback()), this.elapsed += t, this.targetEntity?.transform) {
419
+ const i = this.elapsed / 1e3 * 4 * Math.PI * 2, e = 1 + Math.sin(i) * this.pulseAmplitude;
1553
420
  this.targetEntity.transform.setScale([
1554
- this._baseScale[0] * i,
1555
- this._baseScale[1] * i,
421
+ this._baseScale[0] * e,
422
+ this._baseScale[1] * e,
1556
423
  this._baseScale[2]
1557
424
  ]), this.targetEntity.version++;
1558
425
  }
@@ -1560,13 +427,13 @@ class De extends ne {
1560
427
  }
1561
428
  async _beginPlayback() {
1562
429
  if (this.targetEntity?.transform) {
1563
- const i = this.targetEntity.transform.getScale();
1564
- this._baseScale = [i.x, i.y, i.z];
430
+ const e = this.targetEntity.transform.getScale();
431
+ this._baseScale = [e.x, e.y, e.z];
1565
432
  }
1566
- const e = await this.prepareAudio();
1567
- this.subtitles && this.subtitles.syncWithAudio(e);
1568
- const t = this.targetEntity?.scene;
1569
- await e.play(t, {
433
+ const t = await this.prepareAudio();
434
+ this.subtitles && this.subtitles.syncWithAudio(t);
435
+ const i = this.targetEntity?.scene;
436
+ await t.play(i, {
1570
437
  duckingRatio: this.duckingRatio,
1571
438
  volume: this.volume
1572
439
  });
@@ -1575,38 +442,41 @@ class De extends ne {
1575
442
  this.targetEntity?.transform && (this.targetEntity.transform.setScale(this._baseScale), this.targetEntity.version++);
1576
443
  }
1577
444
  }
1578
- async function Oe(a, e, t = {}) {
1579
- const i = a?.audio || a;
1580
- i && typeof i.awaitUnlock == "function" && await i.awaitUnlock();
1581
- const r = await j.synthesize(e, {
1582
- voice: t.voice,
1583
- speed: t.speed ?? 1,
1584
- pitch: t.pitch,
1585
- volume: t.volume
445
+ async function M(r, t, i = {}) {
446
+ const e = r?.audio || r;
447
+ e && typeof e.awaitUnlock == "function" && await e.awaitUnlock();
448
+ const s = await p.synthesize(t, {
449
+ voice: i.voice,
450
+ speed: i.speed,
451
+ pitch: i.pitch,
452
+ volume: i.volume,
453
+ displayText: i.displayText,
454
+ elevenlabs: i.elevenlabs
1586
455
  });
1587
- return t.subtitles && t.subtitles.syncWithAudio(r), r.play(i, {
1588
- duckingRatio: t.duckingRatio ?? 0.25,
1589
- volume: t.volume ?? 1
1590
- }), a && typeof a.wait == "function" && await a.wait(r.durationMs), r;
456
+ return i.subtitles && i.subtitles.syncWithAudio(s), s.play(e, {
457
+ duckingRatio: i.duckingRatio ?? 0.25,
458
+ volume: i.volume ?? 1
459
+ }), r && typeof r.wait == "function" && await r.wait(s.durationMs), s;
1591
460
  }
1592
461
  export {
1593
- L as DEFAULT_MSEDGE_VOICE,
1594
- K as MSEDGE_VOICES,
1595
- Me as MsEdgeTTSProvider,
1596
- Ne as SUBTITLE_PRESETS,
1597
- De as Speak,
1598
- Re as Subtitles,
1599
- j as TTS,
1600
- G as TTSAudio,
1601
- T as TTSCache,
1602
- Z as TTSClient,
1603
- Fe as TTSNarrationComponent,
1604
- oe as encodeWAV,
1605
- ue as fetchRemoteMsEdgeVoices,
1606
- ce as findMsEdgeVoice,
1607
- W as getAvailableMsEdgeVoices,
1608
- Te as getMsEdgeVoicesByLanguage,
1609
- H as parseMp3Metadata,
1610
- he as setDynamicMsEdgeVoices,
1611
- Oe as speak
462
+ I as DEFAULT_MSEDGE_VOICE,
463
+ W as ElevenLabsTTSProvider,
464
+ N as MSEDGE_VOICES,
465
+ A as MsEdgeTTSProvider,
466
+ v as SUBTITLE_PRESETS,
467
+ T as Speak,
468
+ R as Subtitles,
469
+ p as TTS,
470
+ D as TTSAudio,
471
+ L as TTSCache,
472
+ H as TTSClient,
473
+ F as TTSNarrationComponent,
474
+ P as encodeWAV,
475
+ Y as fetchRemoteMsEdgeVoices,
476
+ X as findMsEdgeVoice,
477
+ Z as getAvailableMsEdgeVoices,
478
+ q as getMsEdgeVoicesByLanguage,
479
+ G as parseMp3Metadata,
480
+ U as setDynamicMsEdgeVoices,
481
+ M as speak
1612
482
  };