retiam 2.0.2 → 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 (35) hide show
  1. package/agent/AGENTS.md +4 -1
  2. package/agent/skill/SKILL.md +7 -4
  3. package/agent/skill/examples/NarratedMotion.ts +10 -2
  4. package/agent/skill/references/api.md +4 -2
  5. package/agent/skill/references/explainers.md +4 -2
  6. package/agent/skill/references/quality.md +3 -0
  7. package/agent/skill/references/tts.md +154 -60
  8. package/bin/lesson.js +39 -12
  9. package/dist/{UTIF-BrQ3Ng5l.min.js → UTIF-D6vx2UEX.min.js} +1 -1
  10. package/dist/{UTIF-CN0-pEnd.js → UTIF-mkffT70R.js} +1 -1
  11. package/dist/{index-yQIdejOj.js → index-CdBPrYbr.js} +3818 -3785
  12. package/dist/{index-3sAseSJQ.min.js → index-jgFyrc2D.min.js} +175 -175
  13. package/dist/libs/explainer/index.js +74 -73
  14. package/dist/libs/explainer/plan.js +162 -140
  15. package/dist/libs/explainer/types.d.ts +5 -0
  16. package/dist/libs/tts/animations/Speak.d.ts +13 -7
  17. package/dist/libs/tts/components/TTSNarrationComponent.d.ts +9 -7
  18. package/dist/libs/tts/core/TTSCache.d.ts +3 -3
  19. package/dist/libs/tts/core/TTSClient.d.ts +3 -3
  20. package/dist/libs/tts/core/types.d.ts +48 -6
  21. package/dist/libs/tts/index.d.ts +2 -1
  22. package/dist/libs/tts/index.js +165 -672
  23. package/dist/libs/tts/node/index.d.ts +10 -0
  24. package/dist/libs/tts/node/index.js +791 -0
  25. package/dist/libs/tts/providers/ElevenLabsTTSProvider.d.ts +30 -0
  26. package/dist/libs/tts/providers/ITTSProvider.d.ts +2 -2
  27. package/dist/libs/vite/index.js +1 -1
  28. package/dist/{main-lQwYAed8.min.js → main-Cc4QQbSK.min.js} +1 -1
  29. package/dist/{main-BTk_9jtl.js → main-DMu_5Qy7.js} +1 -1
  30. package/dist/{msEdgeV2-BLEnvPJa.js → msEdgeV2-D3w4Xsn-.js} +30 -29
  31. package/dist/retiam.es.js +1 -1
  32. package/dist/retiam.es.min.js +1 -1
  33. package/dist/src/engine/entities/composites/text/mathjaxInterop.d.ts +6 -0
  34. package/package.json +4 -2
  35. package/readme.md +20 -1
@@ -1,543 +1,12 @@
1
- import { D as M, c as D, p as F, g as L, s as V, f as P, a as N } from "../../msEdgeV2-BLEnvPJa.js";
2
- import { M as rt, b as ot } from "../../msEdgeV2-BLEnvPJa.js";
3
- import { Component as j, Color as y, hex as l, VGroup as H, SDFText as I, CENTER as $, Rect as X, Animation as K } 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";
4
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. */
5
- function Y(c, t = 24e3) {
6
- const a = t * 2, r = c.length * 2, n = new ArrayBuffer(44 + r), o = new DataView(n);
7
- B(o, 0, "RIFF"), o.setUint32(4, 36 + r, !0), B(o, 8, "WAVE"), B(o, 12, "fmt "), o.setUint32(16, 16, !0), o.setUint16(20, 1, !0), o.setUint16(22, 1, !0), o.setUint32(24, t, !0), o.setUint32(28, a, !0), o.setUint16(32, 2, !0), o.setUint16(34, 16, !0), B(o, 36, "data"), o.setUint32(40, r, !0);
8
- let u = 44;
9
- for (let h = 0; h < c.length; h++) {
10
- const d = Math.max(-1, Math.min(1, c[h])), p = d < 0 ? d * 32768 : d * 32767;
11
- o.setInt16(u, p, !0), u += 2;
12
- }
13
- return n;
14
- }
15
- function B(c, t, e) {
16
- for (let i = 0; i < e.length; i++)
17
- c.setUint8(t + i, e.charCodeAt(i));
18
- }
19
- class R {
20
- text;
21
- phonemes;
22
- samples;
23
- sampleRate;
24
- duration;
25
- durationMs;
26
- voice;
27
- speed;
28
- wordBoundaries;
29
- rawBuffer;
30
- mimeType;
31
- _wavBuffer = null;
32
- _blob = null;
33
- _objectUrl = null;
34
- _dataUrl = null;
35
- constructor(t) {
36
- this.text = t.text, this.phonemes = t.phonemes || "", this.samples = t.samples ?? new Float32Array(0), this.sampleRate = t.sampleRate ?? 24e3, this.voice = t.voice ?? "af_heart", this.speed = t.speed ?? 1, this.wordBoundaries = t.wordBoundaries, this.rawBuffer = t.rawBuffer, this.mimeType = t.mimeType || (t.rawBuffer ? "audio/mpeg" : "audio/wav"), t.durationMs !== void 0 ? (this.durationMs = t.durationMs, this.duration = t.duration ?? t.durationMs / 1e3) : t.duration !== void 0 ? (this.duration = t.duration, this.durationMs = t.duration * 1e3) : (this.duration = this.samples.length > 0 ? this.samples.length / this.sampleRate : 0, this.durationMs = this.duration * 1e3);
37
- }
38
- /**
39
- * Converts raw PCM samples or decodes underlying rawBuffer into a Web Audio [[AudioBuffer]].
40
- */
41
- async getOrDecodeAudioBuffer(t) {
42
- if (this.samples && this.samples.length > 0)
43
- return this.toAudioBuffer(t);
44
- if (this.rawBuffer)
45
- try {
46
- const e = await t.decodeAudioData(this.rawBuffer.slice(0));
47
- return this.samples = e.getChannelData(0), this.sampleRate = e.sampleRate, e;
48
- } catch (e) {
49
- console.warn("[TTSAudio] Failed to decode rawBuffer via context.decodeAudioData:", e);
50
- }
51
- return this.toAudioBuffer(t);
52
- }
53
- /**
54
- * Converts raw PCM samples to a Web Audio [[AudioBuffer]].
55
- */
56
- toAudioBuffer(t) {
57
- const e = t || (typeof window < "u" ? window.__retiam_audio_context : null);
58
- if (e) {
59
- const s = e.createBuffer(1, Math.max(1, this.samples.length), this.sampleRate);
60
- return s.getChannelData(0).set(this.samples), s;
61
- }
62
- const i = typeof window < "u" ? window.AudioContext || window.webkitAudioContext : null;
63
- if (i) {
64
- const a = new i().createBuffer(1, Math.max(1, this.samples.length), this.sampleRate);
65
- return a.getChannelData(0).set(this.samples), a;
66
- }
67
- throw new Error("[TTSAudio] Cannot create AudioBuffer: No AudioContext available in this environment.");
68
- }
69
- /**
70
- * Returns an ArrayBuffer containing encoded 16-bit PCM WAV audio.
71
- */
72
- toWav() {
73
- return this._wavBuffer || (this.rawBuffer && this.mimeType.includes("wav") ? this._wavBuffer = this.rawBuffer : this._wavBuffer = Y(this.samples, this.sampleRate)), this._wavBuffer;
74
- }
75
- /**
76
- * Returns a standard WAV or MP3 [[Blob]].
77
- */
78
- toBlob() {
79
- 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;
80
- }
81
- /**
82
- * Generates a base64 Data URL ("data:audio/...;base64,...").
83
- */
84
- toDataUrl() {
85
- if (!this._dataUrl) {
86
- const t = this.rawBuffer ? new Uint8Array(this.rawBuffer) : new Uint8Array(this.toWav());
87
- let e = "";
88
- const i = t.byteLength;
89
- for (let a = 0; a < i; a++)
90
- e += String.fromCharCode(t[a]);
91
- const s = typeof btoa < "u" ? btoa(e) : Buffer.from(e, "binary").toString("base64");
92
- this._dataUrl = `data:${this.mimeType};base64,${s}`;
93
- }
94
- return this._dataUrl;
95
- }
96
- /**
97
- * Creates or returns a cached Object URL ("blob:...") for this audio.
98
- */
99
- toObjectUrl() {
100
- if (!this._objectUrl)
101
- if (typeof URL < "u" && typeof URL.createObjectURL == "function")
102
- this._objectUrl = URL.createObjectURL(this.toBlob());
103
- else
104
- return this.toDataUrl();
105
- return this._objectUrl;
106
- }
107
- /**
108
- * Revokes any generated Object URL to release memory.
109
- */
110
- revokeObjectUrl() {
111
- this._objectUrl && typeof URL < "u" && typeof URL.revokeObjectURL == "function" && (URL.revokeObjectURL(this._objectUrl), this._objectUrl = null);
112
- }
113
- /**
114
- * Triggers a browser download or writes out the audio file.
115
- */
116
- save(t) {
117
- const e = this.mimeType.includes("mpeg") || this.mimeType.includes("mp3") ? ".mp3" : ".wav", i = `speech${e}`, s = t ? t.includes(".") ? t : `${t}${e}` : i;
118
- if (typeof document < "u") {
119
- const a = this.toObjectUrl(), r = document.createElement("a");
120
- r.style.display = "none", r.href = a, r.download = s, document.body.appendChild(r), r.click(), setTimeout(() => {
121
- document.body.removeChild(r);
122
- }, 100);
123
- return;
124
- }
125
- try {
126
- const a = globalThis.require ? globalThis.require("fs") : null;
127
- if (a) {
128
- const r = this.rawBuffer ? Buffer.from(this.rawBuffer) : Buffer.from(this.toWav());
129
- a.writeFileSync(s, r);
130
- }
131
- } catch (a) {
132
- console.warn(`[TTSAudio] Unable to save file ${s}:`, a);
133
- }
134
- }
135
- /**
136
- * Plays the audio through Retiam's [[AudioManager]] on the dedicated voiceover bus
137
- * with automatic BGM ducking and timeline synchronization.
138
- */
139
- async play(t, e = {}) {
140
- let i;
141
- return t && (typeof t.playVoiceover == "function" ? i = t : t.audio && typeof t.audio.playVoiceover == "function" && (i = t.audio)), i ? i.playVoiceover(this, e) : (console.warn("[TTSAudio] No active AudioManager provided to play(). Creating standalone audio playback."), this._playStandalone(e));
142
- }
143
- /**
144
- * Standalone playback fallback if no Retiam scene/AudioManager is provided.
145
- */
146
- _playStandalone(t) {
147
- return new Promise((e) => {
148
- if (typeof Audio > "u") {
149
- e(this.durationMs);
150
- return;
151
- }
152
- const i = new Audio(this.toObjectUrl());
153
- i.volume = Math.max(0, Math.min(1, t.volume ?? 1)), i.onended = () => {
154
- const s = t.padEndMs ?? 100;
155
- setTimeout(() => e(this.durationMs + s), s);
156
- }, i.onerror = () => {
157
- e(this.durationMs);
158
- }, i.play().catch(() => {
159
- e(this.durationMs);
160
- });
161
- });
162
- }
163
- }
164
- class C {
165
- maxSize;
166
- cache = /* @__PURE__ */ new Map();
167
- constructor(t = 128) {
168
- this.maxSize = Math.max(1, t);
169
- }
170
- static createKey(t, e, i) {
171
- return `${e}:${i.toFixed(2)}:${t.trim().toLowerCase()}`;
172
- }
173
- get(t, e, i) {
174
- const s = C.createKey(t, e, i), a = this.cache.get(s);
175
- if (a)
176
- return this.cache.delete(s), this.cache.set(s, a), a;
177
- }
178
- set(t, e, i, s) {
179
- const a = C.createKey(t, e, i);
180
- if (this.cache.has(a))
181
- this.cache.delete(a);
182
- else if (this.cache.size >= this.maxSize) {
183
- const r = this.cache.keys().next().value;
184
- r && (this.cache.get(r)?.revokeObjectUrl(), this.cache.delete(r));
185
- }
186
- this.cache.set(a, s);
187
- }
188
- has(t, e, i) {
189
- return this.cache.has(C.createKey(t, e, i));
190
- }
191
- clear() {
192
- for (const t of this.cache.values())
193
- t.revokeObjectUrl();
194
- this.cache.clear();
195
- }
196
- get size() {
197
- return this.cache.size;
198
- }
199
- }
200
- class Z {
201
- name = "msedge-tts";
202
- _config;
203
- _cache;
204
- _isBrowser;
205
- _nodeClients = [];
206
- constructor(t = {}) {
207
- this._isBrowser = typeof window < "u" && typeof window.document < "u", this._config = {
208
- cacheAudio: t.cacheAudio !== !1,
209
- maxCacheSize: t.maxCacheSize ?? 128,
210
- msedgeEndpoint: t.msedgeEndpoint || "/api/tts/msedge"
211
- }, this._cache = new C(this._config.maxCacheSize);
212
- }
213
- /**
214
- * Microsoft Edge TTS is cloud-hosted and always ready immediately without local weight loading.
215
- */
216
- get isReady() {
217
- return !0;
218
- }
219
- /**
220
- * Initializes provider configuration. No weight downloading required.
221
- */
222
- async init(t) {
223
- t?.msedgeEndpoint && (this._config.msedgeEndpoint = t.msedgeEndpoint), t?.cacheAudio !== void 0 && (this._config.cacheAudio = t.cacheAudio);
224
- }
225
- /**
226
- * Synthesizes text into high-fidelity speech audio using Microsoft Edge neural voices.
227
- */
228
- async synthesize(t, e = {}) {
229
- const i = t.trim(), s = e.voice || M, a = Math.max(0.25, Math.min(4, e.speed ?? 1));
230
- if (!i)
231
- return new R({
232
- text: "",
233
- samples: new Float32Array(0),
234
- sampleRate: 24e3,
235
- voice: s,
236
- speed: a
237
- });
238
- if (this._config.cacheAudio) {
239
- const n = this._cache.get(i, s, a);
240
- if (n)
241
- return n;
242
- }
243
- const r = this._isBrowser ? await this._synthesizeBrowser(i, s, a, e) : await this._synthesizeNode(i, s, a, e);
244
- return this._config.cacheAudio && this._cache.set(i, s, a, r), r;
245
- }
246
- /**
247
- * Node.js execution: directly calls the msedge-tts package in-process.
248
- */
249
- async _synthesizeNode(t, e, i, s) {
250
- const { MsEdgeTTS: a, OUTPUT_FORMAT: r } = await import("msedge-tts");
251
- D(a);
252
- const n = new a(), o = Math.round((i - 1) * 100), u = o >= 0 ? `+${o}%` : `${o}%`;
253
- await n.setMetadata(e, r.AUDIO_24KHZ_96KBITRATE_MONO_MP3, {
254
- wordBoundaryEnabled: !0,
255
- sentenceBoundaryEnabled: !0
256
- });
257
- const h = {
258
- rate: u
259
- };
260
- if (s.pitch !== void 0 && (h.pitch = s.pitch), s.volume !== void 0 && s.volume !== null) {
261
- if (typeof s.volume == "number")
262
- h.volume = s.volume <= 1 && s.volume > 0 ? Math.round(s.volume * 100) : Math.round(s.volume);
263
- else if (typeof s.volume == "string") {
264
- const b = parseFloat(s.volume);
265
- !isNaN(b) && !s.volume.includes("%") && !s.volume.startsWith("+") && !s.volume.startsWith("-") ? h.volume = b <= 1 && b > 0 ? Math.round(b * 100) : Math.round(b) : h.volume = s.volume;
266
- }
267
- }
268
- const { audioStream: d, metadataStream: p } = n.toStream(t, h), f = [], x = [];
269
- p && p.on("data", (b) => {
270
- try {
271
- const A = b.toString(), S = JSON.parse(A);
272
- if (Array.isArray(S?.Metadata)) {
273
- for (const v of S.Metadata)
274
- if (v.Type === "WordBoundary" && v.Data) {
275
- const U = (v.Data.Offset || 0) / 1e4, z = (v.Data.Duration || 0) / 1e4, W = v.Data.text?.Text || "";
276
- x.push({
277
- text: W,
278
- offsetMs: U,
279
- durationMs: z,
280
- boundaryType: "WordBoundary"
281
- });
282
- }
283
- }
284
- } catch {
285
- }
286
- }), await new Promise((b, A) => {
287
- d.on("data", (S) => {
288
- f.push(S);
289
- }), d.on("end", () => {
290
- b();
291
- }), d.on("error", (S) => {
292
- A(S);
293
- });
294
- });
295
- const k = Buffer.concat(f), g = k.buffer.slice(
296
- k.byteOffset,
297
- k.byteOffset + k.byteLength
298
- ), m = F(new Uint8Array(g));
299
- return new R({
300
- text: t,
301
- rawBuffer: g,
302
- mimeType: "audio/mpeg",
303
- samples: new Float32Array(m.totalSamples),
304
- sampleRate: m.sampleRate,
305
- duration: m.durationSec,
306
- durationMs: m.durationMs,
307
- voice: e,
308
- speed: i,
309
- wordBoundaries: x.length > 0 ? x : void 0
310
- });
311
- }
312
- /**
313
- * Browser execution: invokes the Vite development server proxy endpoint.
314
- */
315
- async _synthesizeBrowser(t, e, i, s) {
316
- const a = `${this._config.msedgeEndpoint}/synthesize`, r = await fetch(a, {
317
- method: "POST",
318
- headers: {
319
- "Content-Type": "application/json"
320
- },
321
- body: JSON.stringify({
322
- text: t,
323
- voice: e,
324
- speed: i,
325
- pitch: s.pitch,
326
- volume: s.volume
327
- })
328
- });
329
- if (!r.ok)
330
- throw new Error(`[MsEdgeTTSProvider] Synthesis failed: HTTP ${r.status} ${r.statusText}`);
331
- const n = r.headers.get("X-Audio-Duration-Ms"), o = r.headers.get("X-Word-Boundaries");
332
- let u;
333
- if (o)
334
- try {
335
- u = JSON.parse(decodeURIComponent(o));
336
- } catch {
337
- }
338
- const h = await r.arrayBuffer();
339
- let d = new Float32Array(0), p = 24e3, f = n ? parseFloat(n) : 0;
340
- const x = window.AudioContext || window.webkitAudioContext;
341
- if (x) {
342
- const k = !!window.__retiam_audio_context, g = window.__retiam_audio_context || new x();
343
- try {
344
- const m = await g.decodeAudioData(h.slice(0));
345
- d = m.getChannelData(0), p = m.sampleRate, f || (f = m.duration * 1e3);
346
- } catch (m) {
347
- console.warn("[MsEdgeTTSProvider] AudioContext decode deferred, using raw MP3 buffer:", m);
348
- } finally {
349
- !k && g && typeof g.close == "function" && g.close().catch(() => {
350
- });
351
- }
352
- }
353
- if (d.length === 0 || !f) {
354
- const k = F(new Uint8Array(h));
355
- p = k.sampleRate, f || (f = k.durationMs);
356
- }
357
- return new R({
358
- text: t,
359
- rawBuffer: h,
360
- mimeType: "audio/mpeg",
361
- samples: d,
362
- sampleRate: p,
363
- duration: f / 1e3,
364
- durationMs: f,
365
- voice: e,
366
- speed: i,
367
- wordBoundaries: u
368
- });
369
- }
370
- /**
371
- * Progressively streams synthesized speech sentence by sentence.
372
- */
373
- async *stream(t, e = {}) {
374
- const i = e.voice || M, s = Math.max(0.25, Math.min(4, e.speed ?? 1)), a = e.splitPattern || /([.!?;]+(?:\s+|$))/g, r = [], n = t.split(a);
375
- for (let h = 0; h < n.length; h += 2) {
376
- const d = (n[h] + (n[h + 1] || "")).trim();
377
- d.length > 0 && r.push(d);
378
- }
379
- r.length === 0 && r.push(t.trim());
380
- let o = 0, u = 0;
381
- for (const h of r) {
382
- const d = await this.synthesize(h, {
383
- ...e,
384
- voice: i,
385
- speed: s
386
- });
387
- yield {
388
- text: h,
389
- phonemes: "",
390
- audio: d,
391
- chunkIndex: o++,
392
- startTimeMs: u
393
- }, u += d.durationMs;
394
- }
395
- }
396
- /**
397
- * Returns curated Microsoft Edge neural voices immediately.
398
- */
399
- getVoices() {
400
- return L();
401
- }
402
- /**
403
- * Fetches the complete live catalog of all 320+ Microsoft Edge neural voices.
404
- */
405
- async fetchVoices() {
406
- if (this._isBrowser)
407
- try {
408
- const t = await fetch(`${this._config.msedgeEndpoint}/voices`);
409
- if (t.ok) {
410
- const e = await t.json();
411
- if (Array.isArray(e))
412
- return V(e), e;
413
- }
414
- } catch {
415
- }
416
- return P();
417
- }
418
- /**
419
- * Clears cached utterances.
420
- */
421
- clearCache() {
422
- this._cache.clear();
423
- }
424
- /**
425
- * Disposes resources and releases cache.
426
- */
427
- async dispose() {
428
- this.clearCache();
429
- for (const t of this._nodeClients)
430
- try {
431
- t.close();
432
- } catch {
433
- }
434
- this._nodeClients = [];
435
- }
436
- }
437
- class O {
438
- _provider;
439
- _config;
440
- constructor(t = {}, e) {
441
- this._config = t, this._provider = e ?? new Z(t);
442
- }
443
- /** The active speech provider. */
444
- get provider() {
445
- return this._provider;
446
- }
447
- /** Whether the active provider is ready. */
448
- get isReady() {
449
- return this._provider.isReady;
450
- }
451
- /** Replaces the active provider instance. */
452
- setProvider(t) {
453
- this._provider = t;
454
- }
455
- /** Initializes the active provider. */
456
- async init(t) {
457
- t && (this._config = t), await this._provider.init(t ?? this._config);
458
- }
459
- /** Synthesizes text with the active provider. */
460
- async synthesize(t, e = {}) {
461
- return this._provider.synthesize(t, e);
462
- }
463
- /** Streams synthesized audio sentence by sentence. */
464
- async *stream(t, e = {}) {
465
- yield* this._provider.stream(t, e);
466
- }
467
- /** Synthesizes and plays speech through a Scene or AudioManager. */
468
- async speak(t, e, i = {}) {
469
- const s = await this.synthesize(e, i), a = await s.play(t, i);
470
- return { audio: s, durationMs: a };
471
- }
472
- /** Returns voices exposed by the active provider. */
473
- getVoices() {
474
- return this._provider.getVoices();
475
- }
476
- /** Finds a voice by ID, display name, or partial match. */
477
- findVoice(t) {
478
- const e = t.toLowerCase();
479
- return this._provider.getVoices().find(
480
- (i) => i.id.toLowerCase() === e || i.name.toLowerCase() === e || i.id.toLowerCase().includes(e) || i.name.toLowerCase().includes(e)
481
- ) ?? N(t);
482
- }
483
- /** Releases provider resources and cached audio. */
484
- async dispose() {
485
- await this._provider.dispose();
486
- }
487
- }
488
- let E = null;
489
- function w() {
490
- return E || (E = new O()), E;
491
- }
492
- class T {
493
- /** Replaces the global client configuration and restores the built-in provider. */
494
- static configure(t) {
495
- E = new O(t);
496
- }
497
- /** Initializes the global speech provider. */
498
- static async init(t) {
499
- t && this.configure(t), await w().init(t);
500
- }
501
- /** Replaces the global provider instance. */
502
- static setProvider(t) {
503
- w().setProvider(t);
504
- }
505
- /** Active global provider. */
506
- static get provider() {
507
- return w().provider;
508
- }
509
- /** Synthesizes text to speech. */
510
- static async synthesize(t, e) {
511
- return w().synthesize(t, e);
512
- }
513
- /** Explicit alias for Microsoft Edge neural synthesis. */
514
- static async msedge(t, e) {
515
- return w().synthesize(t, e);
516
- }
517
- /** Streams synthesized speech sentence by sentence. */
518
- static async *stream(t, e) {
519
- yield* w().stream(t, e);
520
- }
521
- /** Synthesizes and plays speech through a Scene or AudioManager. */
522
- static async speak(t, e, i) {
523
- return w().speak(t, e, i);
524
- }
525
- /** Returns the active provider's voices. */
526
- static getVoices() {
527
- return w().getVoices();
528
- }
529
- /** Finds a Microsoft Edge neural voice. */
530
- static findVoice(t) {
531
- return w().findVoice(t);
532
- }
533
- /** Recommended default voice. */
534
- static DEFAULT_VOICE = M;
535
- /** Explicit provider-specific alias for the recommended default voice. */
536
- static DEFAULT_MSEDGE_VOICE = M;
537
- }
538
- class Q extends j {
6
+ class F extends x {
539
7
  voice;
540
8
  speed;
9
+ elevenlabs;
541
10
  animateSpeaking;
542
11
  pulseAmplitude;
543
12
  pulseFrequency;
@@ -548,7 +17,7 @@ class Q extends j {
548
17
  _baseScaleY = 1;
549
18
  _baseScaleZ = 1;
550
19
  constructor(t = {}) {
551
- super(), this.voice = t.voice ?? "en-US-JennyNeural", this.speed = t.speed ?? 1, this.animateSpeaking = t.animateSpeaking !== !1, this.pulseAmplitude = t.pulseAmplitude ?? 0.05, this.pulseFrequency = t.pulseFrequency ?? 4, this.onSpeechStart = t.onSpeechStart, this.onSpeechEnd = t.onSpeechEnd;
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;
552
21
  }
553
22
  onInit() {
554
23
  if (this.entity?.transform) {
@@ -569,20 +38,29 @@ class Q extends j {
569
38
  * @param overrides Voice and playback option overrides.
570
39
  * @returns Synthesized [[TTSAudioResult]] and total speech duration in milliseconds.
571
40
  */
572
- async speak(t, e = {}) {
573
- const i = this.entity?.scene, s = e.voice ?? this.voice, a = e.speed ?? this.speed;
41
+ async speak(t, i = {}) {
42
+ const e = this.entity?.scene, s = i.voice ?? this.voice, c = i.speed ?? this.speed;
574
43
  if (this.entity?.transform) {
575
- const o = this.entity.transform.getScale();
576
- 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;
577
46
  }
578
- const r = await T.synthesize(t, { voice: s, speed: a, ...e });
579
- this._isSpeaking = !0, this.onSpeechStart?.(t, r);
580
- const n = await r.play(i, e);
581
- return this._isSpeaking = !1, this._restoreBaseScale(), this.onSpeechEnd?.(t), { audio: r, durationMs: n };
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 };
582
60
  }
583
- update(t, e) {
61
+ update(t, i) {
584
62
  if (!(!this.enabled || !this.entity?.transform) && this._isSpeaking && this.animateSpeaking) {
585
- const i = t / 1e3 * this.pulseFrequency * Math.PI * 2, s = 1 + Math.sin(i) * this.pulseAmplitude;
63
+ const e = t / 1e3 * this.pulseFrequency * Math.PI * 2, s = 1 + Math.sin(e) * this.pulseAmplitude;
586
64
  this.entity.transform.setScale([
587
65
  this._baseScaleX * s,
588
66
  this._baseScaleY * s,
@@ -601,14 +79,14 @@ class Q extends j {
601
79
  this._isSpeaking = !1, this._restoreBaseScale();
602
80
  }
603
81
  }
604
- const q = {
82
+ const v = {
605
83
  obsidian: {
606
84
  fontSize: 0.38,
607
- textColor: l("#F8FAFC"),
85
+ textColor: a("#F8FAFC"),
608
86
  textOpacity: 1,
609
- backdropColor: l("#060913"),
87
+ backdropColor: a("#060913"),
610
88
  backdropOpacity: 0.85,
611
- strokeColor: new y(0.22, 0.74, 0.97, 0.35),
89
+ strokeColor: new d(0.22, 0.74, 0.97, 0.35),
612
90
  strokeWidth: 0.02,
613
91
  cornerRadius: 0.22,
614
92
  pillShape: !1,
@@ -619,11 +97,11 @@ const q = {
619
97
  },
620
98
  pill: {
621
99
  fontSize: 0.38,
622
- textColor: l("#FFFFFF"),
100
+ textColor: a("#FFFFFF"),
623
101
  textOpacity: 1,
624
- backdropColor: l("#080D1A"),
102
+ backdropColor: a("#080D1A"),
625
103
  backdropOpacity: 0.88,
626
- strokeColor: new y(0.38, 0.52, 0.75, 0.4),
104
+ strokeColor: new d(0.38, 0.52, 0.75, 0.4),
627
105
  strokeWidth: 0.02,
628
106
  pillShape: !0,
629
107
  padding: [0.7, 0.28],
@@ -633,11 +111,11 @@ const q = {
633
111
  },
634
112
  minimal: {
635
113
  fontSize: 0.38,
636
- textColor: l("#F1F5F9"),
114
+ textColor: a("#F1F5F9"),
637
115
  textOpacity: 1,
638
- backdropColor: l("#090D16"),
116
+ backdropColor: a("#090D16"),
639
117
  backdropOpacity: 0.8,
640
- strokeColor: y.NONE,
118
+ strokeColor: d.NONE,
641
119
  strokeWidth: 0,
642
120
  cornerRadius: 0.16,
643
121
  pillShape: !1,
@@ -648,11 +126,11 @@ const q = {
648
126
  },
649
127
  broadcast: {
650
128
  fontSize: 0.38,
651
- textColor: l("#FFFFFF"),
129
+ textColor: a("#FFFFFF"),
652
130
  textOpacity: 1,
653
- backdropColor: l("#000000"),
131
+ backdropColor: a("#000000"),
654
132
  backdropOpacity: 0.92,
655
- strokeColor: y.NONE,
133
+ strokeColor: d.NONE,
656
134
  strokeWidth: 0,
657
135
  cornerRadius: 0.08,
658
136
  pillShape: !1,
@@ -663,11 +141,11 @@ const q = {
663
141
  },
664
142
  neon: {
665
143
  fontSize: 0.38,
666
- textColor: l("#E0F2FE"),
144
+ textColor: a("#E0F2FE"),
667
145
  textOpacity: 1,
668
- backdropColor: l("#030712"),
146
+ backdropColor: a("#030712"),
669
147
  backdropOpacity: 0.92,
670
- strokeColor: l("#00F0FF"),
148
+ strokeColor: a("#00F0FF"),
671
149
  strokeWidth: 0.025,
672
150
  cornerRadius: 0.24,
673
151
  pillShape: !1,
@@ -678,11 +156,11 @@ const q = {
678
156
  },
679
157
  glass: {
680
158
  fontSize: 0.38,
681
- textColor: l("#FFFFFF"),
159
+ textColor: a("#FFFFFF"),
682
160
  textOpacity: 1,
683
- backdropColor: new y(0.05, 0.09, 0.15, 0.65),
161
+ backdropColor: new d(0.05, 0.09, 0.15, 0.65),
684
162
  backdropOpacity: 0.65,
685
- strokeColor: new y(1, 1, 1, 0.25),
163
+ strokeColor: new d(1, 1, 1, 0.25),
686
164
  strokeWidth: 0.015,
687
165
  cornerRadius: 0.25,
688
166
  pillShape: !1,
@@ -693,11 +171,11 @@ const q = {
693
171
  },
694
172
  light: {
695
173
  fontSize: 0.38,
696
- textColor: l("#0F172A"),
174
+ textColor: a("#0F172A"),
697
175
  textOpacity: 1,
698
- backdropColor: l("#F8FAFC"),
176
+ backdropColor: a("#F8FAFC"),
699
177
  backdropOpacity: 0.92,
700
- strokeColor: new y(0.39, 0.45, 0.55, 0.25),
178
+ strokeColor: new d(0.39, 0.45, 0.55, 0.25),
701
179
  strokeWidth: 0.015,
702
180
  cornerRadius: 0.2,
703
181
  pillShape: !1,
@@ -707,18 +185,18 @@ const q = {
707
185
  showBackdrop: !0
708
186
  }
709
187
  };
710
- function _(c, t) {
711
- return c ? typeof c == "string" ? l(c) : c : t;
188
+ function n(r, t) {
189
+ return r ? typeof r == "string" ? a(r) : r : t;
712
190
  }
713
- class tt extends H {
191
+ class R extends b {
714
192
  _textEntity;
715
193
  _backdrop = null;
716
194
  _fontSize = 0.38;
717
- _textColor = l("#F8FAFC");
195
+ _textColor = a("#F8FAFC");
718
196
  _textOpacity = 1;
719
- _backdropColor = l("#060913");
197
+ _backdropColor = a("#060913");
720
198
  _backdropOpacity = 0.85;
721
- _strokeColor = new y(0.22, 0.74, 0.97, 0.35);
199
+ _strokeColor = new d(0.22, 0.74, 0.97, 0.35);
722
200
  _strokeWidth = 0.02;
723
201
  _showBorder = !0;
724
202
  _cornerRadius = 0.22;
@@ -731,14 +209,14 @@ class tt extends H {
731
209
  _clearTimeout = null;
732
210
  constructor(t = {}) {
733
211
  super();
734
- const e = t.zIndex ?? 999;
735
- this._textEntity = new I("", {
212
+ const i = t.zIndex ?? 999;
213
+ this._textEntity = new m("", {
736
214
  fontSize: this._fontSize,
737
215
  fill: this._textColor,
738
- anchor: $
739
- }), this._textEntity.paint.z_index = e + 1, this._backdrop = new X(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 = e, this._backdrop.paint.opacity = 0, this.add(this._backdrop), this.add(this._textEntity), this.paint.z_index = e, this.applyOptions(t);
740
- const i = t.bottomOffset ?? -5;
741
- 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]);
742
220
  }
743
221
  /** Access the underlying SDFText entity for advanced low-level styling */
744
222
  get textEntity() {
@@ -752,8 +230,8 @@ class tt extends H {
752
230
  * Applies a complete configuration options object or partial updates.
753
231
  */
754
232
  applyOptions(t = {}) {
755
- const i = { ...t.preset ? q[t.preset] : {}, ...t };
756
- return i.fontSize !== void 0 && (this._fontSize = i.fontSize, this._textEntity.fontSize = i.fontSize), i.textColor !== void 0 && (this._textColor = _(i.textColor, this._textColor), this._textEntity.paint.fill = this._textColor), i.textOpacity !== void 0 && (this._textOpacity = i.textOpacity), i.backdropColor !== void 0 && (this._backdropColor = _(i.backdropColor, this._backdropColor)), i.backdropOpacity !== void 0 && (this._backdropOpacity = i.backdropOpacity), i.strokeColor !== void 0 && (this._strokeColor = _(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;
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;
757
235
  }
758
236
  /** Set an aesthetic preset theme */
759
237
  setPreset(t) {
@@ -768,39 +246,39 @@ class tt extends H {
768
246
  return this._pillShape = t, this._textEntity.text && this._updateBackdropDimensions(this._textEntity.text), this;
769
247
  }
770
248
  /** Set the inner horizontal and vertical padding around the subtitle text */
771
- setPadding(t, e = t) {
772
- return this._padding = [t, e], 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;
773
251
  }
774
252
  /** Configure backdrop fill color and opacity */
775
- setBackdropColor(t, e) {
776
- return this._backdropColor = _(t, this._backdropColor), e !== void 0 && (this._backdropOpacity = Math.max(0, Math.min(1, e))), 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;
777
255
  }
778
- setStroke(t, e) {
779
- return typeof t == "number" ? (this._strokeWidth = t, this._showBorder = t > 0, e !== void 0 && (typeof e == "string" || e instanceof y) && (this._strokeColor = _(e, this._strokeColor))) : (this._strokeColor = _(t, this._strokeColor), typeof e == "number" && (this._strokeWidth = e, this._showBorder = e > 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;
780
258
  }
781
259
  /** Set backdrop border stroke color */
782
260
  setStrokeColor(t) {
783
- return this._strokeColor = _(t, this._strokeColor), this._backdrop && (this._backdrop.paint.stroke = this._strokeColor, this._backdrop.invalidate()), this;
261
+ return this._strokeColor = n(t, this._strokeColor), this._backdrop && (this._backdrop.paint.stroke = this._strokeColor, this._backdrop.invalidate()), this;
784
262
  }
785
263
  /** Set backdrop border stroke width */
786
264
  setStrokeWidth(t) {
787
265
  return this._strokeWidth = t, this._showBorder = t > 0, this._backdrop && (this._backdrop.paint.strokeWidth = this._strokeWidth, this._backdrop.invalidate()), this;
788
266
  }
789
267
  /** Configure subtitle typography (font color and world unit font size) */
790
- setTextColor(t, e = 1) {
791
- return this._textColor = _(t, this._textColor), this._textOpacity = e, this._textEntity.paint.fill = this._textColor, this._textEntity.paint.opacity = e, 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;
792
270
  }
793
271
  /** Set subtitle font size in world units */
794
272
  setFontSize(t) {
795
273
  return t > 0 && (this._fontSize = t, this._textEntity.fontSize = t, this._textEntity.text && this._updateBackdropDimensions(this._textEntity.text)), this;
796
274
  }
797
275
  /** Configure backdrop visual styling (colors, stroke border, corner radius) */
798
- setBackdropStyle(t, e, i, s) {
799
- return this._backdropColor = _(t, this._backdropColor), e !== void 0 && (this._strokeColor = _(e, this._strokeColor), this._showBorder = !0), i !== void 0 && (this._strokeWidth = i, this._showBorder = i > 0), s !== void 0 && (this._cornerRadius = s, 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;
800
278
  }
801
279
  /** Configure subtitle typography */
802
- setTextStyle(t, e) {
803
- return this._textColor = _(t, this._textColor), this._textEntity.paint.fill = this._textColor, e !== void 0 && e > 0 && (this._fontSize = e, this._textEntity.fontSize = e), 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;
804
282
  }
805
283
  /** Set maximum characters per line before word-wrapping occurs */
806
284
  setMaxLineChars(t) {
@@ -818,14 +296,14 @@ class tt extends H {
818
296
  * Computes safe corner radius: clamped so circular arcs never self-intersect,
819
297
  * or computes stadium capsule radius when pillShape is enabled.
820
298
  */
821
- _getEffectiveCornerRadius(t, e) {
822
- const i = Math.min(t, e) / 2;
823
- return this._pillShape ? i : typeof this._cornerRadius == "number" ? Math.max(0, Math.min(this._cornerRadius, i)) : Array.isArray(this._cornerRadius) ? [
824
- Math.max(0, Math.min(this._cornerRadius[0], i)),
825
- Math.max(0, Math.min(this._cornerRadius[1], i)),
826
- Math.max(0, Math.min(this._cornerRadius[2], i)),
827
- Math.max(0, Math.min(this._cornerRadius[3], i))
828
- ] : 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);
829
307
  }
830
308
  /**
831
309
  * Internal geometry reflow: updates backdrop width & height using genuine Rect
@@ -837,35 +315,35 @@ class tt extends H {
837
315
  this._backdrop.paint.opacity = 0, this._backdrop.invalidate();
838
316
  return;
839
317
  }
840
- const e = this._textEntity.getGroupLocalAABB(), i = e.isEmpty() ? 0 : e.size.x, s = e.isEmpty() ? 0 : e.size.y, [a, r] = this._padding;
841
- let n = i, o = s;
842
- if (n <= 0.1 || o <= 0.1) {
843
- const p = t.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(`
844
322
  `);
845
- n = Math.max(...p.map((x) => x.length)) * (this._fontSize * 0.44), o = p.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);
846
324
  }
847
- const u = Math.max(2.4, Math.min(this._maxWidth, n + a * 2)), h = o + r * 2, d = this._getEffectiveCornerRadius(u, h);
848
- this._backdrop.transform.setScale([1, 1, 1]), this._backdrop.width = u, this._backdrop.height = h, this._backdrop.cornerRadius = d, this._backdrop.paint.fill = this._backdropColor, this._backdrop.paint.stroke = this._showBorder ? this._strokeColor : y.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();
849
327
  }
850
328
  /**
851
329
  * Displays a subtitle sentence, formatting lines with word-wrapping and
852
330
  * reflowing the backdrop pill with true circular corner arcs.
853
331
  */
854
- showText(t, e) {
332
+ showText(t, i) {
855
333
  this._clearTimeout && (clearTimeout(this._clearTimeout), this._clearTimeout = null);
856
- const i = t.trim();
857
- let s = i;
858
- if (i.length > this._maxLineChars) {
859
- const a = i.split(" "), r = [];
860
- let n = "";
861
- for (const o of a)
862
- (n + " " + o).trim().length > this._maxLineChars ? (n && r.push(n), n = o) : n = n ? n + " " + o : o;
863
- n && r.push(n), s = r.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(`
864
342
  `);
865
343
  }
866
- return this._textEntity.text = s, this._textEntity.paint.opacity = this._textOpacity, this._updateBackdropDimensions(s), this.version++, e && e > 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(() => {
867
345
  this.clear();
868
- }, e)), this;
346
+ }, i)), this;
869
347
  }
870
348
  /**
871
349
  * Synchronizes this subtitle banner with a [[TTSAudioResult]], displaying the spoken text
@@ -881,7 +359,7 @@ class tt extends H {
881
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;
882
360
  }
883
361
  }
884
- class et extends K {
362
+ class T extends C {
885
363
  text;
886
364
  voice;
887
365
  speed;
@@ -892,12 +370,21 @@ class et extends K {
892
370
  duckingRatio;
893
371
  volume;
894
372
  _audioResult = null;
373
+ _audioTask = null;
374
+ _synthesisOptions;
895
375
  _isStarted = !1;
896
376
  _baseScale = [1, 1, 1];
897
- constructor(t, e = {}) {
898
- super(e), this.text = t, this.voice = e.voice ?? "en-US-JennyNeural", this.speed = e.speed ?? 1, this.pitch = e.pitch, this.subtitles = e.subtitles, this.targetEntity = e.entity, this.pulseAmplitude = e.pulseAmplitude ?? 0.05, this.duckingRatio = e.duckingRatio ?? 0.25, this.volume = e.volume ?? 1;
899
- const i = t.trim().split(/\s+/).length;
900
- 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));
901
388
  }
902
389
  /**
903
390
  * Prepares animation state.
@@ -912,12 +399,15 @@ class et extends K {
912
399
  * Synthesizes audio ahead of time and locks exact duration.
913
400
  */
914
401
  async prepareAudio() {
915
- return this._audioResult || (this._audioResult = await T.synthesize(this.text, {
916
- voice: this.voice,
917
- speed: this.speed,
918
- pitch: this.pitch,
919
- volume: this.volume
920
- }), 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;
921
411
  }
922
412
  update(t) {
923
413
  if (this.paused) return !1;
@@ -926,10 +416,10 @@ class et extends K {
926
416
  t = -this.delay, this.delay = 0;
927
417
  }
928
418
  if (this._isStarted || (this._isStarted = !0, this._beginPlayback()), this.elapsed += t, this.targetEntity?.transform) {
929
- const e = this.elapsed / 1e3 * 4 * Math.PI * 2, i = 1 + Math.sin(e) * this.pulseAmplitude;
419
+ const i = this.elapsed / 1e3 * 4 * Math.PI * 2, e = 1 + Math.sin(i) * this.pulseAmplitude;
930
420
  this.targetEntity.transform.setScale([
931
- this._baseScale[0] * i,
932
- this._baseScale[1] * i,
421
+ this._baseScale[0] * e,
422
+ this._baseScale[1] * e,
933
423
  this._baseScale[2]
934
424
  ]), this.targetEntity.version++;
935
425
  }
@@ -937,13 +427,13 @@ class et extends K {
937
427
  }
938
428
  async _beginPlayback() {
939
429
  if (this.targetEntity?.transform) {
940
- const i = this.targetEntity.transform.getScale();
941
- this._baseScale = [i.x, i.y, i.z];
430
+ const e = this.targetEntity.transform.getScale();
431
+ this._baseScale = [e.x, e.y, e.z];
942
432
  }
943
433
  const t = await this.prepareAudio();
944
434
  this.subtitles && this.subtitles.syncWithAudio(t);
945
- const e = this.targetEntity?.scene;
946
- await t.play(e, {
435
+ const i = this.targetEntity?.scene;
436
+ await t.play(i, {
947
437
  duckingRatio: this.duckingRatio,
948
438
  volume: this.volume
949
439
  });
@@ -952,38 +442,41 @@ class et extends K {
952
442
  this.targetEntity?.transform && (this.targetEntity.transform.setScale(this._baseScale), this.targetEntity.version++);
953
443
  }
954
444
  }
955
- async function it(c, t, e = {}) {
956
- const i = c?.audio || c;
957
- i && typeof i.awaitUnlock == "function" && await i.awaitUnlock();
958
- const s = await T.synthesize(t, {
959
- voice: e.voice,
960
- speed: e.speed ?? 1,
961
- pitch: e.pitch,
962
- volume: e.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
963
455
  });
964
- return e.subtitles && e.subtitles.syncWithAudio(s), s.play(i, {
965
- duckingRatio: e.duckingRatio ?? 0.25,
966
- volume: e.volume ?? 1
967
- }), c && typeof c.wait == "function" && await c.wait(s.durationMs), s;
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;
968
460
  }
969
461
  export {
970
- M as DEFAULT_MSEDGE_VOICE,
971
- rt as MSEDGE_VOICES,
972
- Z as MsEdgeTTSProvider,
973
- q as SUBTITLE_PRESETS,
974
- et as Speak,
975
- tt as Subtitles,
976
- T as TTS,
977
- R as TTSAudio,
978
- C as TTSCache,
979
- O as TTSClient,
980
- Q as TTSNarrationComponent,
981
- Y as encodeWAV,
982
- P as fetchRemoteMsEdgeVoices,
983
- N as findMsEdgeVoice,
984
- L as getAvailableMsEdgeVoices,
985
- ot as getMsEdgeVoicesByLanguage,
986
- F as parseMp3Metadata,
987
- V as setDynamicMsEdgeVoices,
988
- it 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
989
482
  };