@pie-players/pie-tool-protractor 0.3.43 → 0.3.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,7 +18,7 @@ A draggable and rotatable protractor overlay tool for geometry and measurement q
18
18
 
19
19
  ```svelte
20
20
  <script>
21
- import '@pie-players/pie-tool-protractor/components/tool-protractor-element';
21
+ import '@pie-players/pie-tool-protractor';
22
22
  import { toolCoordinator } from '@pie-players/pie-assessment-toolkit';
23
23
 
24
24
  let showProtractor = $derived(
@@ -1,15 +1,15 @@
1
- import { t as l } from "./defineProperty-CyepwRr5.js";
2
- function w(e, t) {
3
- if (typeof e != "string" || e.length === 0) return null;
1
+ import { t as d } from "./defineProperty-Dt7ri80W.js";
2
+ function T(t, e) {
3
+ if (typeof t != "string" || t.length === 0) return null;
4
4
  try {
5
- const r = typeof t.apiEndpoint == "string" && /^https?:\/\//i.test(t.apiEndpoint) ? t.apiEndpoint : void 0, i = r ? new URL(e, r) : new URL(e);
5
+ const r = typeof e.apiEndpoint == "string" && /^https?:\/\//i.test(e.apiEndpoint) ? e.apiEndpoint : void 0, i = r ? new URL(t, r) : new URL(t);
6
6
  return i.protocol !== "http:" && i.protocol !== "https:" ? null : i;
7
7
  } catch {
8
8
  return null;
9
9
  }
10
10
  }
11
- function k(e, t) {
12
- const r = Array.isArray(t.assetOrigins) ? t.assetOrigins.filter((i) => typeof i == "string" && i.length > 0).map((i) => {
11
+ function A(t, e) {
12
+ const r = Array.isArray(e.assetOrigins) ? e.assetOrigins.filter((i) => typeof i == "string" && i.length > 0).map((i) => {
13
13
  try {
14
14
  return new URL(i).origin;
15
15
  } catch {
@@ -17,85 +17,89 @@ function k(e, t) {
17
17
  }
18
18
  }) : [];
19
19
  if (r.length === 0) {
20
- if (typeof t.apiEndpoint == "string" && /^https?:\/\//i.test(t.apiEndpoint)) try {
21
- return new URL(t.apiEndpoint).origin === e.origin;
20
+ if (typeof e.apiEndpoint == "string" && /^https?:\/\//i.test(e.apiEndpoint)) try {
21
+ return new URL(e.apiEndpoint).origin === t.origin;
22
22
  } catch {
23
23
  return !1;
24
24
  }
25
- return typeof window < "u" && typeof window.location?.origin == "string" && window.location.origin === e.origin;
25
+ return typeof window < "u" && typeof window.location?.origin == "string" && window.location.origin === t.origin;
26
26
  }
27
- return r.includes(e.origin);
27
+ return r.includes(t.origin);
28
28
  }
29
- function A(e) {
30
- const t = {};
31
- for (const [r, i] of Object.entries(e)) {
29
+ function R(t) {
30
+ const e = {};
31
+ for (const [r, i] of Object.entries(t)) {
32
32
  const o = r.toLowerCase();
33
- o === "authorization" || o === "proxy-authorization" || o === "cookie" || o.startsWith("x-auth-") || (t[r] = i);
33
+ o === "authorization" || o === "proxy-authorization" || o === "cookie" || o.startsWith("x-auth-") || (e[r] = i);
34
34
  }
35
- return t;
35
+ return e;
36
36
  }
37
- var R = (e) => {
38
- const t = (e.providerOptions && typeof e.providerOptions == "object" ? e.providerOptions : {}).__pieTelemetry;
39
- return typeof t == "function" ? t : void 0;
40
- }, M = {
37
+ var I = (t) => {
38
+ const e = (t.providerOptions && typeof t.providerOptions == "object" ? t.providerOptions : {}).__pieTelemetry;
39
+ return typeof e == "function" ? e : void 0;
40
+ }, E = {
41
41
  pie: 3e3,
42
42
  custom: 3e3
43
- }, T = (e) => e.replace(/\/+$/, ""), E = (e) => {
44
- const t = T(e.apiEndpoint), r = (e.provider || "").toLowerCase();
45
- return r === "polly" || r === "google" ? `${t}/${r}/voices` : `${t}/voices`;
46
- }, b = (e) => e.transportMode === "custom" ? "custom" : e.transportMode === "pie" ? "pie" : e.provider === "custom" ? "custom" : "pie", S = (e, t) => e.endpointMode ? e.endpointMode : t === "custom" ? "rootPost" : "synthesizePath", U = (e, t) => e.endpointValidationMode ? e.endpointValidationMode : t === "custom" ? "none" : "voices", O = (e) => {
47
- const t = e.providerOptions || {};
48
- if (typeof t.speedRate == "string") return t.speedRate;
49
- const r = Number(e.rate ?? 1);
43
+ }, M = /* @__PURE__ */ new Set(["polly", "google"]), P = (t) => {
44
+ if (!t || b(t) !== "pie") return !1;
45
+ const e = (t.provider || "polly").toLowerCase();
46
+ return M.has(e);
47
+ }, S = (t) => t.replace(/\/+$/, ""), O = (t) => {
48
+ const e = S(t.apiEndpoint), r = (t.provider || "").toLowerCase();
49
+ return r === "polly" || r === "google" ? `${e}/${r}/voices` : `${e}/voices`;
50
+ }, b = (t) => t.transportMode === "custom" ? "custom" : t.transportMode === "pie" ? "pie" : t.provider === "custom" ? "custom" : "pie", k = (t, e) => t.endpointMode ? t.endpointMode : e === "custom" ? "rootPost" : "synthesizePath", U = (t, e) => t.endpointValidationMode ? t.endpointValidationMode : e === "custom" ? "none" : "voices", L = (t) => {
51
+ const e = t.providerOptions || {};
52
+ if (typeof e.speedRate == "string") return e.speedRate;
53
+ const r = Number(t.rate ?? 1);
50
54
  return !Number.isFinite(r) || r <= 0.95 ? "slow" : r >= 1.5 ? "fast" : "medium";
51
- }, P = (e) => {
52
- const t = [];
55
+ }, B = (t) => {
56
+ const e = [];
53
57
  let r = 0;
54
- const i = e.split(`
58
+ const i = t.split(`
55
59
  `).map((o) => o.trim()).filter(Boolean);
56
60
  for (const o of i) try {
57
- const s = JSON.parse(o), a = typeof s.type == "string" ? s.type : "word", d = typeof s.time == "number" && Number.isFinite(s.time) ? s.time : 0, n = typeof s.value == "string" ? s.value : "", h = typeof s.start == "number" && Number.isFinite(s.start) ? s.start : null, u = typeof s.end == "number" && Number.isFinite(s.end) ? s.end : null, p = h ?? r, c = u ?? p + Math.max(1, n.length || String(s.value || "").length);
58
- r = Math.max(c + 1, r), t.push({
59
- time: d,
61
+ const s = JSON.parse(o), a = typeof s.type == "string" ? s.type : "word", n = typeof s.time == "number" && Number.isFinite(s.time) ? s.time : 0, h = typeof s.value == "string" ? s.value : "", l = typeof s.start == "number" && Number.isFinite(s.start) ? s.start : null, u = typeof s.end == "number" && Number.isFinite(s.end) ? s.end : null, y = l ?? r, c = u ?? y + Math.max(1, h.length || String(s.value || "").length);
62
+ r = Math.max(c + 1, r), e.push({
63
+ time: n,
60
64
  type: a,
61
- start: p,
65
+ start: y,
62
66
  end: c,
63
- value: n
67
+ value: h
64
68
  });
65
69
  } catch {
66
70
  }
67
- return t;
68
- }, C = (e) => {
69
- if (!Array.isArray(e)) return [];
70
- const t = [];
71
+ return e;
72
+ }, C = (t) => {
73
+ if (!Array.isArray(t)) return [];
74
+ const e = [];
71
75
  let r = 0;
72
- for (const i of e) {
76
+ for (const i of t) {
73
77
  if (!i || typeof i != "object") continue;
74
- const o = typeof i.type == "string" ? i.type : "word", s = typeof i.time == "number" && Number.isFinite(i.time) ? i.time : 0, a = typeof i.value == "string" ? i.value : "", d = typeof i.start == "number" && Number.isFinite(i.start) ? i.start : null, n = typeof i.end == "number" && Number.isFinite(i.end) ? i.end : null, h = d ?? r, u = n ?? h + Math.max(1, a.length || 1);
75
- r = Math.max(r, u + 1), t.push({
78
+ const o = typeof i.type == "string" ? i.type : "word", s = typeof i.time == "number" && Number.isFinite(i.time) ? i.time : 0, a = typeof i.value == "string" ? i.value : "", n = typeof i.start == "number" && Number.isFinite(i.start) ? i.start : null, h = typeof i.end == "number" && Number.isFinite(i.end) ? i.end : null, l = n ?? r, u = h ?? l + Math.max(1, a.length || 1);
79
+ r = Math.max(r, u + 1), e.push({
76
80
  time: s,
77
81
  type: o,
78
- start: h,
82
+ start: l,
79
83
  end: u,
80
84
  value: a
81
85
  });
82
86
  }
83
- return t.sort((i, o) => i.time !== o.time ? i.time - o.time : i.start !== o.start ? i.start - o.start : i.end - o.end);
84
- }, x = {
87
+ return e.sort((i, o) => i.time !== o.time ? i.time - o.time : i.start !== o.start ? i.start - o.start : i.end - o.end);
88
+ }, z = {
85
89
  pie: {
86
90
  id: "pie",
87
- resolveSynthesisUrl: (e) => {
88
- const t = S(e, "pie"), r = T(e.apiEndpoint);
89
- return t === "rootPost" ? r : `${r}/synthesize`;
91
+ resolveSynthesisUrl: (t) => {
92
+ const e = k(t, "pie"), r = S(t.apiEndpoint);
93
+ return e === "rootPost" ? r : `${r}/synthesize`;
90
94
  },
91
- buildRequestBody: (e, t) => {
92
- const r = t.providerOptions || {}, i = typeof t.engine == "string" ? t.engine : typeof r.engine == "string" ? r.engine : void 0, o = typeof r.sampleRate == "number" && Number.isFinite(r.sampleRate) ? r.sampleRate : void 0, s = r.format === "mp3" || r.format === "ogg" || r.format === "pcm" ? r.format : void 0, a = Array.isArray(r.speechMarkTypes) ? r.speechMarkTypes.filter((d) => d === "word" || d === "sentence" || d === "ssml") : void 0;
95
+ buildRequestBody: (t, e) => {
96
+ const r = e.providerOptions || {}, i = typeof e.engine == "string" ? e.engine : typeof r.engine == "string" ? r.engine : void 0, o = typeof r.sampleRate == "number" && Number.isFinite(r.sampleRate) ? r.sampleRate : void 0, s = r.format === "mp3" || r.format === "ogg" || r.format === "pcm" ? r.format : void 0, a = Array.isArray(r.speechMarkTypes) ? r.speechMarkTypes.filter((n) => n === "word" || n === "sentence" || n === "ssml") : void 0;
93
97
  return {
94
- text: e,
95
- provider: t.provider || "polly",
96
- voice: t.voice,
97
- language: t.language,
98
- rate: t.rate,
98
+ text: t,
99
+ provider: e.provider || "polly",
100
+ voice: e.voice,
101
+ language: e.language,
102
+ rate: e.rate,
99
103
  engine: i,
100
104
  sampleRate: o,
101
105
  format: s,
@@ -103,100 +107,96 @@ var R = (e) => {
103
107
  includeSpeechMarks: !0
104
108
  };
105
109
  },
106
- parseResponse: async (e) => {
107
- const t = await e.json();
110
+ parseResponse: async (t) => {
111
+ const e = await t.json();
108
112
  return {
109
113
  audio: {
110
114
  kind: "base64",
111
- data: t.audio,
112
- contentType: t.contentType
115
+ data: e.audio,
116
+ contentType: e.contentType
113
117
  },
114
- speechMarks: Array.isArray(t.speechMarks) ? t.speechMarks : []
118
+ speechMarks: Array.isArray(e.speechMarks) ? e.speechMarks : []
115
119
  };
116
120
  }
117
121
  },
118
122
  custom: {
119
123
  id: "custom",
120
- resolveSynthesisUrl: (e) => {
121
- const t = S(e, "custom"), r = T(e.apiEndpoint);
122
- return t === "synthesizePath" ? `${r}/synthesize` : r;
124
+ resolveSynthesisUrl: (t) => {
125
+ const e = k(t, "custom"), r = S(t.apiEndpoint);
126
+ return e === "synthesizePath" ? `${r}/synthesize` : r;
123
127
  },
124
- buildRequestBody: (e, t) => {
125
- const r = t.providerOptions || {}, i = typeof r.lang_id == "string" ? r.lang_id : t.language || "en-US", o = typeof r.cache == "boolean" ? r.cache : !0;
128
+ buildRequestBody: (t, e) => {
129
+ const r = e.providerOptions || {}, i = typeof r.lang_id == "string" ? r.lang_id : e.language || "en-US", o = typeof r.cache == "boolean" ? r.cache : !0;
126
130
  return {
127
- text: e,
128
- speedRate: O(t),
131
+ text: t,
132
+ speedRate: L(e),
129
133
  lang_id: i,
130
134
  cache: o
131
135
  };
132
136
  },
133
- parseResponse: async (e, t, r, i) => {
134
- const o = await e.json(), s = {};
135
- if (t.includeAuthOnAssetFetch)
136
- for (const [h, u] of Object.entries(r)) h.toLowerCase() === "authorization" && (s[h] = u);
137
+ parseResponse: async (t, e, r, i) => {
138
+ const o = await t.json(), s = {};
139
+ if (e.includeAuthOnAssetFetch)
140
+ for (const [l, u] of Object.entries(r)) l.toLowerCase() === "authorization" && (s[l] = u);
137
141
  let a = [];
138
- const d = C(o.speechMarks);
139
- if (d.length > 0) a = d;
142
+ const n = C(o.speechMarks);
143
+ if (n.length > 0) a = n;
140
144
  else if (typeof o.word == "string" && o.word.length > 0) {
141
- const h = w(o.word, t);
142
- if (h !== null) {
143
- const u = k(h, t) ? s : A(s), p = await fetch(h.toString(), {
145
+ const l = T(o.word, e);
146
+ if (l !== null) {
147
+ const u = A(l, e) ? s : R(s), y = await fetch(l.toString(), {
144
148
  headers: u,
145
149
  signal: i,
146
150
  redirect: "error"
147
151
  });
148
- p.ok && (a = P(await p.text()));
152
+ y.ok && (a = B(await y.text()));
149
153
  }
150
154
  }
151
- const n = w(o.audioContent, t);
152
- if (n === null) throw new Error("TTS server returned an invalid audio URL");
155
+ const h = T(o.audioContent, e);
156
+ if (h === null) throw new Error("TTS server returned an invalid audio URL");
153
157
  return {
154
158
  audio: {
155
159
  kind: "url",
156
- url: n.toString()
160
+ url: h.toString()
157
161
  },
158
162
  speechMarks: a
159
163
  };
160
164
  }
161
165
  }
162
- }, z = class {
163
- constructor(e, t) {
164
- l(this, "config", void 0), l(this, "adapter", void 0), l(this, "currentAudio", null), l(this, "pausedState", !1), l(this, "wordTimings", []), l(this, "highlightInterval", null), l(this, "intentionallyStopped", !1), l(this, "activeSynthesisController", null), l(this, "synthesisRunId", 0), l(this, "telemetryReporter", void 0), l(this, "onWordBoundary", void 0), this.config = e, this.adapter = t, this.telemetryReporter = R(e);
166
+ }, x = class {
167
+ constructor(t, e) {
168
+ d(this, "config", void 0), d(this, "adapter", void 0), d(this, "currentAudio", null), d(this, "pausedState", !1), d(this, "wordTimings", []), d(this, "highlightInterval", null), d(this, "intentionallyStopped", !1), d(this, "activeSynthesisController", null), d(this, "synthesisRunId", 0), d(this, "telemetryReporter", void 0), d(this, "onWordBoundary", void 0), this.config = t, this.adapter = e, this.telemetryReporter = I(t);
165
169
  }
166
- async emitTelemetry(e, t) {
170
+ async emitTelemetry(t, e) {
167
171
  try {
168
- await this.telemetryReporter?.(e, t);
172
+ await this.telemetryReporter?.(t, e);
169
173
  } catch (r) {
170
174
  console.warn("[ServerTTSProvider] telemetry callback failed:", r);
171
175
  }
172
176
  }
173
- async speak(e) {
177
+ async speak(t) {
174
178
  this.stop(), this.intentionallyStopped = !1;
175
- const t = ++this.synthesisRunId, r = new AbortController();
179
+ const e = ++this.synthesisRunId, r = new AbortController();
176
180
  this.activeSynthesisController = r;
177
- const { audioUrl: i, wordTimings: o } = await this.synthesizeSpeech(e, r.signal, t);
178
- if (t !== this.synthesisRunId) {
181
+ const { audioUrl: i, wordTimings: o } = await this.synthesizeSpeech(t, r.signal, e);
182
+ if (e !== this.synthesisRunId) {
179
183
  URL.revokeObjectURL(i);
180
184
  return;
181
185
  }
182
- const s = this.config.rate || 1;
183
- return this.wordTimings = o.map((a) => ({
184
- ...a,
185
- time: a.time / s
186
- })), new Promise((a, d) => {
186
+ return this.wordTimings = o, new Promise((s, a) => {
187
187
  const n = new Audio(i);
188
- this.currentAudio = n, this.config.rate && (n.playbackRate = Math.max(0.25, Math.min(4, this.config.rate))), this.config.volume !== void 0 && (n.volume = Math.max(0, Math.min(1, this.config.volume))), n.onplay = () => {
188
+ this.currentAudio = n, this.config.volume !== void 0 && (n.volume = Math.max(0, Math.min(1, this.config.volume))), n.onplay = () => {
189
189
  this.pausedState = !1, this.onWordBoundary && this.wordTimings.length > 0 && this.startWordHighlighting();
190
190
  }, n.onended = () => {
191
- this.stopWordHighlighting(), URL.revokeObjectURL(i), this.currentAudio = null, this.wordTimings = [], a();
191
+ this.stopWordHighlighting(), URL.revokeObjectURL(i), this.currentAudio = null, this.wordTimings = [], s();
192
192
  }, n.onerror = (h) => {
193
- this.stopWordHighlighting(), URL.revokeObjectURL(i), this.currentAudio = null, this.wordTimings = [], this.intentionallyStopped ? a() : d(/* @__PURE__ */ new Error("Failed to play audio from server"));
193
+ this.stopWordHighlighting(), URL.revokeObjectURL(i), this.currentAudio = null, this.wordTimings = [], this.intentionallyStopped ? s() : a(/* @__PURE__ */ new Error("Failed to play audio from server"));
194
194
  }, n.onpause = () => {
195
195
  this.stopWordHighlighting(), this.pausedState = !0;
196
- }, n.play().catch(d);
196
+ }, n.play().catch(a);
197
197
  });
198
198
  }
199
- async synthesizeSpeech(e, t, r) {
199
+ async synthesizeSpeech(t, e, r) {
200
200
  const i = Date.now();
201
201
  await this.emitTelemetry("pie-tool-backend-call-start", {
202
202
  toolId: "tts",
@@ -208,13 +208,13 @@ var R = (e) => {
208
208
  ...this.config.headers
209
209
  };
210
210
  this.config.authToken && (o.Authorization = `Bearer ${this.config.authToken}`);
211
- const s = this.adapter.resolveSynthesisUrl(this.config), a = this.adapter.buildRequestBody(e, this.config), d = await (async () => {
211
+ const s = this.adapter.resolveSynthesisUrl(this.config), a = this.adapter.buildRequestBody(t, this.config), n = await (async () => {
212
212
  try {
213
213
  return await fetch(s, {
214
214
  method: "POST",
215
215
  headers: o,
216
216
  body: JSON.stringify(a),
217
- signal: t
217
+ signal: e
218
218
  });
219
219
  } catch (c) {
220
220
  throw await this.emitTelemetry("pie-tool-backend-call-error", {
@@ -227,77 +227,99 @@ var R = (e) => {
227
227
  }), c;
228
228
  }
229
229
  })();
230
- if (!d.ok) {
231
- const c = await d.json().catch(() => ({})), y = c.message || c.error?.message || `Server returned ${d.status}`;
232
- throw await this.emitTelemetry("pie-tool-backend-call-error", {
230
+ if (!n.ok) {
231
+ const c = await n.text().catch(() => "");
232
+ let p = {};
233
+ if (c) try {
234
+ p = JSON.parse(c);
235
+ } catch (v) {
236
+ console.warn("[ServerTTSProvider] non-OK response body was not JSON", {
237
+ status: n.status,
238
+ url: s,
239
+ rawBodyPreview: c.slice(0, 500),
240
+ parseError: v instanceof Error ? v.message : String(v)
241
+ });
242
+ }
243
+ const g = typeof p.message == "string" && p.message || (typeof p.error?.message == "string" ? p.error.message : void 0) || `Server returned ${n.status}`;
244
+ await this.emitTelemetry("pie-tool-backend-call-error", {
233
245
  toolId: "tts",
234
246
  backend: this.config.provider || "server",
235
247
  operation: "synthesize-speech",
236
248
  duration: Date.now() - i,
237
- statusCode: d.status,
249
+ statusCode: n.status,
238
250
  errorType: "TTSBackendRequestError",
239
- message: y
240
- }), new Error(y);
251
+ message: g
252
+ });
253
+ const m = t.replace(/\s+/g, " ").trim().slice(0, 120);
254
+ throw console.error("[ServerTTSProvider] synthesize request failed", {
255
+ status: n.status,
256
+ url: s,
257
+ backend: this.config.provider || "server",
258
+ message: g,
259
+ responseBody: p,
260
+ textLength: t.length,
261
+ textPreview: m.length < t.length ? `${m}…` : m
262
+ }), new Error(g);
241
263
  }
242
- const n = await this.adapter.parseResponse(d, this.config, o, t);
243
- if (r !== this.synthesisRunId || t.aborted) throw new Error("Synthesis superseded by a newer request");
244
- let h;
245
- if (n.audio.kind === "base64") h = this.base64ToBlob(n.audio.data, n.audio.contentType);
264
+ const h = await this.adapter.parseResponse(n, this.config, o, e);
265
+ if (r !== this.synthesisRunId || e.aborted) throw new Error("Synthesis superseded by a newer request");
266
+ let l;
267
+ if (h.audio.kind === "base64") l = this.base64ToBlob(h.audio.data, h.audio.contentType);
246
268
  else {
247
- const c = n.audio.url, y = Date.now();
269
+ const c = h.audio.url, p = Date.now();
248
270
  await this.emitTelemetry("pie-tool-backend-call-start", {
249
271
  toolId: "tts",
250
272
  backend: this.config.provider || "server",
251
273
  operation: "fetch-synthesized-audio-asset"
252
274
  });
253
- const f = {};
254
- this.config.includeAuthOnAssetFetch && this.config.authToken && (f.Authorization = `Bearer ${this.config.authToken}`);
255
- const v = w(c, this.config);
256
- if (v === null)
275
+ const g = {};
276
+ this.config.includeAuthOnAssetFetch && this.config.authToken && (g.Authorization = `Bearer ${this.config.authToken}`);
277
+ const m = T(c, this.config);
278
+ if (m === null)
257
279
  throw await this.emitTelemetry("pie-tool-backend-call-error", {
258
280
  toolId: "tts",
259
281
  backend: this.config.provider || "server",
260
282
  operation: "fetch-synthesized-audio-asset",
261
- duration: Date.now() - y,
283
+ duration: Date.now() - p,
262
284
  errorType: "TTSAssetInvalidUrl",
263
285
  message: "TTS asset URL rejected (non-http(s) or malformed)"
264
286
  }), new Error("TTS asset URL rejected (non-http(s) or malformed)");
265
- const I = k(v, this.config) ? f : A(f), m = await (async () => {
287
+ const v = A(m, this.config) ? g : R(g), f = await (async () => {
266
288
  try {
267
- return await fetch(v.toString(), {
268
- headers: I,
269
- signal: t,
289
+ return await fetch(m.toString(), {
290
+ headers: v,
291
+ signal: e,
270
292
  redirect: "error"
271
293
  });
272
- } catch (g) {
294
+ } catch (w) {
273
295
  throw await this.emitTelemetry("pie-tool-backend-call-error", {
274
296
  toolId: "tts",
275
297
  backend: this.config.provider || "server",
276
298
  operation: "fetch-synthesized-audio-asset",
277
- duration: Date.now() - y,
299
+ duration: Date.now() - p,
278
300
  errorType: "TTSAssetNetworkError",
279
- message: g instanceof Error ? g.message : String(g)
280
- }), g;
301
+ message: w instanceof Error ? w.message : String(w)
302
+ }), w;
281
303
  }
282
304
  })();
283
- if (!m.ok)
305
+ if (!f.ok)
284
306
  throw await this.emitTelemetry("pie-tool-backend-call-error", {
285
307
  toolId: "tts",
286
308
  backend: this.config.provider || "server",
287
309
  operation: "fetch-synthesized-audio-asset",
288
- duration: Date.now() - y,
289
- statusCode: m.status,
310
+ duration: Date.now() - p,
311
+ statusCode: f.status,
290
312
  errorType: "TTSAssetFetchError",
291
- message: `Failed to download synthesized audio (${m.status})`
292
- }), new Error(`Failed to download synthesized audio (${m.status})`);
293
- h = await m.blob(), await this.emitTelemetry("pie-tool-backend-call-success", {
313
+ message: `Failed to download synthesized audio (${f.status})`
314
+ }), new Error(`Failed to download synthesized audio (${f.status})`);
315
+ l = await f.blob(), await this.emitTelemetry("pie-tool-backend-call-success", {
294
316
  toolId: "tts",
295
317
  backend: this.config.provider || "server",
296
318
  operation: "fetch-synthesized-audio-asset",
297
- duration: Date.now() - y
319
+ duration: Date.now() - p
298
320
  });
299
321
  }
300
- const u = URL.createObjectURL(h), p = this.parseSpeechMarks(n.speechMarks);
322
+ const u = URL.createObjectURL(l), y = this.parseSpeechMarks(h.speechMarks);
301
323
  return await this.emitTelemetry("pie-tool-backend-call-success", {
302
324
  toolId: "tts",
303
325
  backend: this.config.provider || "server",
@@ -305,21 +327,22 @@ var R = (e) => {
305
327
  duration: Date.now() - i
306
328
  }), {
307
329
  audioUrl: u,
308
- wordTimings: p
330
+ wordTimings: y
309
331
  };
310
332
  }
311
- base64ToBlob(e, t) {
312
- const r = atob(e), i = new Array(r.length);
333
+ base64ToBlob(t, e) {
334
+ const r = atob(t), i = new Array(r.length);
313
335
  for (let s = 0; s < r.length; s++) i[s] = r.charCodeAt(s);
314
336
  const o = new Uint8Array(i);
315
- return new Blob([o], { type: t });
337
+ return new Blob([o], { type: e });
316
338
  }
317
- parseSpeechMarks(e) {
318
- return e.filter((t) => t.type === "word").map((t, r) => ({
319
- time: t.time,
339
+ parseSpeechMarks(t) {
340
+ return t.filter((e) => e.type === "word").map((e, r) => ({
341
+ time: e.time,
320
342
  wordIndex: r,
321
- charIndex: t.start,
322
- length: t.end - t.start
343
+ charIndex: e.start,
344
+ length: e.end - e.start,
345
+ word: e.value
323
346
  }));
324
347
  }
325
348
  startWordHighlighting() {
@@ -332,17 +355,17 @@ var R = (e) => {
332
355
  return;
333
356
  }
334
357
  console.log("[ServerTTSProvider] Starting word highlighting with", this.wordTimings.length, "word timings"), console.log("[ServerTTSProvider] Playback rate:", this.currentAudio.playbackRate), console.log("[ServerTTSProvider] First 3 timings:", this.wordTimings.slice(0, 3));
335
- let e = -1;
358
+ let t = -1;
336
359
  this.highlightInterval = window.setInterval(() => {
337
360
  if (!this.currentAudio) {
338
361
  this.stopWordHighlighting();
339
362
  return;
340
363
  }
341
- const t = this.currentAudio.currentTime * 1e3;
364
+ const e = this.currentAudio.currentTime * 1e3;
342
365
  for (let r = 0; r < this.wordTimings.length; r++) {
343
366
  const i = this.wordTimings[r];
344
- if (t >= i.time && r > e) {
345
- this.onWordBoundary && (console.log("[ServerTTSProvider] Highlighting word at charIndex:", i.charIndex, "length:", i.length, "time:", i.time, "currentTime:", t), this.onWordBoundary("", i.charIndex, i.length)), e = r;
367
+ if (e >= i.time && r > t) {
368
+ this.onWordBoundary && (console.log("[ServerTTSProvider] Highlighting word at charIndex:", i.charIndex, "length:", i.length, "time:", i.time, "currentTime:", e), this.onWordBoundary(i.word, i.charIndex, i.length)), t = r;
346
369
  break;
347
370
  }
348
371
  }
@@ -366,63 +389,63 @@ var R = (e) => {
366
389
  isPaused() {
367
390
  return this.pausedState;
368
391
  }
369
- updateSettings(e) {
370
- e.rate !== void 0 && (this.config.rate = e.rate, this.currentAudio && (this.currentAudio.playbackRate = Math.max(0.25, Math.min(4, e.rate)))), e.pitch !== void 0 && (this.config.pitch = e.pitch), e.voice !== void 0 && (this.config.voice = e.voice);
392
+ updateSettings(t) {
393
+ t.rate !== void 0 && (this.config.rate = t.rate), t.pitch !== void 0 && (this.config.pitch = t.pitch), t.voice !== void 0 && (this.config.voice = t.voice);
371
394
  }
372
- }, L = class {
395
+ }, N = class {
373
396
  constructor() {
374
- l(this, "providerId", "server-tts"), l(this, "providerName", "Server TTS"), l(this, "version", "1.0.0"), l(this, "config", null), l(this, "adapter", null), l(this, "telemetryReporter", void 0);
397
+ d(this, "providerId", "server-tts"), d(this, "providerName", "Server TTS"), d(this, "version", "1.0.0"), d(this, "config", null), d(this, "adapter", null), d(this, "telemetryReporter", void 0);
375
398
  }
376
- async emitTelemetry(e, t) {
399
+ async emitTelemetry(t, e) {
377
400
  try {
378
- await this.telemetryReporter?.(e, t);
401
+ await this.telemetryReporter?.(t, e);
379
402
  } catch (r) {
380
403
  console.warn("[ServerTTSProvider] telemetry callback failed:", r);
381
404
  }
382
405
  }
383
- async initialize(e) {
384
- const t = e;
385
- if (!t.apiEndpoint) throw new Error("apiEndpoint is required for ServerTTSProvider");
386
- this.config = t, this.telemetryReporter = R(t);
387
- const r = b(t);
388
- if (this.adapter = x[r], t.validateEndpoint) {
406
+ async initialize(t) {
407
+ const e = t;
408
+ if (!e.apiEndpoint) throw new Error("apiEndpoint is required for ServerTTSProvider");
409
+ this.config = e, this.telemetryReporter = I(e);
410
+ const r = b(e);
411
+ if (this.adapter = z[r], e.validateEndpoint) {
389
412
  const i = Date.now();
390
413
  if (await this.emitTelemetry("pie-tool-backend-call-start", {
391
414
  toolId: "tts",
392
- backend: t.provider || "server",
415
+ backend: e.provider || "server",
393
416
  operation: "validate-endpoint"
394
417
  }), !await this.testAPIAvailability())
395
418
  throw await this.emitTelemetry("pie-tool-backend-call-error", {
396
419
  toolId: "tts",
397
- backend: t.provider || "server",
420
+ backend: e.provider || "server",
398
421
  operation: "validate-endpoint",
399
422
  duration: Date.now() - i,
400
423
  errorType: "TTSEndpointValidationError",
401
- message: `Server TTS API not available at ${t.apiEndpoint}`
402
- }), new Error(`Server TTS API not available at ${t.apiEndpoint}`);
424
+ message: `Server TTS API not available at ${e.apiEndpoint}`
425
+ }), new Error(`Server TTS API not available at ${e.apiEndpoint}`);
403
426
  await this.emitTelemetry("pie-tool-backend-call-success", {
404
427
  toolId: "tts",
405
- backend: t.provider || "server",
428
+ backend: e.provider || "server",
406
429
  operation: "validate-endpoint",
407
430
  duration: Date.now() - i
408
431
  });
409
432
  }
410
- return new z(t, this.adapter);
433
+ return new x(e, this.adapter);
411
434
  }
412
435
  async testAPIAvailability() {
413
436
  if (!this.config || !this.adapter) return !1;
414
437
  try {
415
- const e = { ...this.config.headers };
416
- this.config.authToken && (e.Authorization = `Bearer ${this.config.authToken}`);
417
- const t = new AbortController(), r = setTimeout(() => t.abort(), 5e3), i = U(this.config, this.adapter.id);
438
+ const t = { ...this.config.headers };
439
+ this.config.authToken && (t.Authorization = `Bearer ${this.config.authToken}`);
440
+ const e = new AbortController(), r = setTimeout(() => e.abort(), 5e3), i = U(this.config, this.adapter.id);
418
441
  if (i === "none")
419
442
  return clearTimeout(r), !0;
420
- const o = i === "voices" ? E(this.config) : this.adapter.resolveSynthesisUrl(this.config), s = i === "voices" ? "GET" : "OPTIONS";
443
+ const o = i === "voices" ? O(this.config) : this.adapter.resolveSynthesisUrl(this.config), s = i === "voices" ? "GET" : "OPTIONS";
421
444
  try {
422
445
  const a = await fetch(o, {
423
446
  method: s,
424
- headers: e,
425
- signal: t.signal
447
+ headers: t,
448
+ signal: e.signal
426
449
  });
427
450
  return clearTimeout(r), a.ok || a.status === 405;
428
451
  } catch {
@@ -432,8 +455,8 @@ var R = (e) => {
432
455
  return !1;
433
456
  }
434
457
  }
435
- supportsFeature(e) {
436
- switch (e) {
458
+ supportsFeature(t) {
459
+ switch (t) {
437
460
  case "pause":
438
461
  case "resume":
439
462
  case "wordBoundary":
@@ -447,6 +470,7 @@ var R = (e) => {
447
470
  }
448
471
  }
449
472
  getCapabilities() {
473
+ const t = this.config ? b(this.config) : "pie";
450
474
  return {
451
475
  supportsPause: !0,
452
476
  supportsResume: !0,
@@ -454,7 +478,8 @@ var R = (e) => {
454
478
  supportsVoiceSelection: !0,
455
479
  supportsRateControl: !0,
456
480
  supportsPitchControl: !1,
457
- maxTextLength: M[this.config ? b(this.config) : "pie"]
481
+ supportsSSML: P(this.config),
482
+ maxTextLength: E[t]
458
483
  };
459
484
  }
460
485
  destroy() {
@@ -462,5 +487,5 @@ var R = (e) => {
462
487
  }
463
488
  };
464
489
  export {
465
- L as ServerTTSProvider
490
+ N as ServerTTSProvider
466
491
  };
@@ -1,4 +1,4 @@
1
- import { t as s } from "./defineProperty-CyepwRr5.js";
1
+ import { t as s } from "./defineProperty-Dt7ri80W.js";
2
2
  var n = class {
3
3
  constructor() {
4
4
  s(this, "providerId", "desmos"), s(this, "providerName", "Desmos"), s(this, "supportedTypes", [