@burdenoff/fe-libs 2026.1016.3 → 2026.1017.2
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 +25 -1
- package/dist/shared/assistant/i18nManifest.d.ts +1 -0
- package/dist/shared/assistant/i18nManifest.js +1 -0
- package/dist/shared/assistant/speech/hostedTtsSpeechEngine.d.ts +6 -0
- package/dist/shared/assistant/speech/hostedTtsSpeechEngine.js +118 -101
- package/dist/shared/assistant/speech/notifyCancellationSpeechEngine.js +3 -0
- package/dist/shared/assistant/speech/routingSpeechEngine.js +11 -8
- package/dist/shared/assistant/speech/types.d.ts +11 -0
- package/dist/shared/assistant/ui/AssistantMessageItem.js +110 -100
- package/dist/shared/assistant/ui/AssistantSpeechProvider.d.ts +10 -0
- package/dist/shared/assistant/ui/AssistantSpeechProvider.js +278 -261
- package/dist/shared/assistant/ui/useVoiceSession.js +222 -227
- package/dist/shared/assistant/voice/handsfree/playbackReference.d.ts +15 -4
- package/dist/shared/assistant/voice/handsfree/playbackReference.js +9 -6
- package/dist/shared/pages/AiPage.d.ts +0 -8
- package/dist/shared/pages/AiPage.js +126 -72
- package/dist/shared/pages/ai/CatalogPanel.d.ts +9 -0
- package/dist/shared/pages/ai/CatalogPanel.js +80 -0
- package/dist/shared/pages/ai/ReadNotice.d.ts +4 -0
- package/dist/shared/pages/ai/ReadNotice.js +18 -0
- package/dist/shared/pages/ai/SummaryPanel.d.ts +7 -0
- package/dist/shared/pages/ai/SummaryPanel.js +135 -0
- package/dist/shared/pages/ai/contracts.d.ts +45 -0
- package/dist/shared/pages/ai/contracts.js +159 -0
- package/dist/shared/pages/ai/i18n.d.ts +1 -0
- package/dist/shared/pages/ai/i18n.js +10 -0
- package/dist/shared/pages/ai/useAiRead.d.ts +18 -0
- package/dist/shared/pages/ai/useAiRead.js +57 -0
- package/dist/shared/utils/mockData.js +212 -0
- package/dist/shared-assistant.js +270 -270
- package/package.json +1 -5
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ This is a **Layer 1 package** - a monolithic package with subpath exports for al
|
|
|
21
21
|
|
|
22
22
|
```typescript
|
|
23
23
|
import { Button } from '@burdenoff/fe-libs/button';
|
|
24
|
-
import {
|
|
24
|
+
import { mockProducts } from '@burdenoff/fe-libs/shared/utils/mockData';
|
|
25
25
|
import { cn } from '@burdenoff/fe-libs/shared';
|
|
26
26
|
```
|
|
27
27
|
|
|
@@ -129,3 +129,27 @@ plain browser (persisted in `sessionStorage`, cleared with `off`). It is
|
|
|
129
129
|
- TailwindCSS v4
|
|
130
130
|
- Radix UI (for accessible primitives)
|
|
131
131
|
- Vite 7 (for building)
|
|
132
|
+
|
|
133
|
+
## Export migration (BOFF-9417)
|
|
134
|
+
|
|
135
|
+
Beginning with the first CalVer release containing BOFF-9417, the already
|
|
136
|
+
nonfunctional `@burdenoff/fe-libs/workflow` export metadata is explicitly retired. Both advertised
|
|
137
|
+
targets (`dist/workflow.js` and `dist/workflow.d.ts`) are absent from the verified
|
|
138
|
+
published 2026.1016.3 package. The former WorkflowCanvas/WorkflowNode implementation
|
|
139
|
+
was deliberately removed in source commit `f0920f9a` on 2026-01-15; this change does
|
|
140
|
+
not remove a working runtime implementation or restore that obsolete UI. Existing
|
|
141
|
+
published releases are unchanged. Usage outside the bounded local repository
|
|
142
|
+
inventory is unknown; this is not a claim that external consumers do not exist.
|
|
143
|
+
|
|
144
|
+
Current workflow-builder functionality belongs to `@burdenoff/fluidgrids-fe-sdk`
|
|
145
|
+
(WorkflowBuilder and WorkflowBuilderCore), not another microfrontend import. Those
|
|
146
|
+
APIs differ from the deleted generic canvas/node props and require an explicit
|
|
147
|
+
consumer migration; they are not a drop-in compatibility adapter. Consumers should
|
|
148
|
+
review their intended workflow behavior before choosing that API.
|
|
149
|
+
|
|
150
|
+
The existing `@burdenoff/fe-libs/shared/utils/mockData` subpath is retained. Its five
|
|
151
|
+
fixture arrays now have an explicit runtime build entry alongside their declarations.
|
|
152
|
+
The archive verifier checks literal export/bin targets in the actual npm tar before
|
|
153
|
+
publication; wildcard export patterns and universal consumer compatibility are not
|
|
154
|
+
certified by that existence check. Normal release automation assigns the concrete
|
|
155
|
+
CalVer version; this source note does not claim publication or host adoption.
|
|
@@ -499,6 +499,7 @@ export declare const ASSISTANT_I18N_LITERAL_COPY: {
|
|
|
499
499
|
readonly "sheet.collapse": "Shrink the assistant";
|
|
500
500
|
readonly "sheet.expand": "Expand the assistant";
|
|
501
501
|
readonly "speech.codeBlock": "Code block";
|
|
502
|
+
readonly "speech.deviceFallback": "Device voice used: the selected voice was unavailable.";
|
|
502
503
|
readonly "speech.emailAt": "at";
|
|
503
504
|
readonly "speech.emailDot": "dot";
|
|
504
505
|
readonly "speech.image": "image";
|
|
@@ -393,6 +393,7 @@ var m = {
|
|
|
393
393
|
"sheet.collapse": "Shrink the assistant",
|
|
394
394
|
"sheet.expand": "Expand the assistant",
|
|
395
395
|
"speech.codeBlock": "Code block",
|
|
396
|
+
"speech.deviceFallback": "Device voice used: the selected voice was unavailable.",
|
|
396
397
|
"speech.emailAt": "at",
|
|
397
398
|
"speech.emailDot": "dot",
|
|
398
399
|
"speech.image": "image",
|
|
@@ -60,4 +60,10 @@ export declare const HOSTED_OUTPUT_STALL_AUDIO_RATIO = 2;
|
|
|
60
60
|
export declare const HOSTED_OUTPUT_MIN_AUDIO_BYTES_PER_SECOND = 1000;
|
|
61
61
|
/** The watchdog's window once `bytesHanded` bytes of audio went to the output. */
|
|
62
62
|
export declare function hostedOutputStallMs(bytesHanded: number): number;
|
|
63
|
+
/** 44 bytes of silence: what a tap plays to unlock an engine's element. */
|
|
64
|
+
export declare const SILENT_WAV_DATA_URI = "data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA=";
|
|
65
|
+
/** Unlock every hosted engine's playback element. Call inside a user gesture. */
|
|
66
|
+
export declare function primeHostedPlayback(): void;
|
|
67
|
+
/** Test seam: forget every engine registered so far. */
|
|
68
|
+
export declare function resetHostedPlaybackForTest(): void;
|
|
63
69
|
export declare function createHostedTtsEngine(options: HostedTtsOptions): HostedSpeechEngine;
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { HOSTED_TTS_TIER as
|
|
3
|
-
import { buildRequest as
|
|
4
|
-
import { createPieceSink as
|
|
1
|
+
import { isPlaybackReferenceAttached as e, tapPlaybackElement as t } from "../voice/handsfree/playbackReference.js";
|
|
2
|
+
import { HOSTED_TTS_TIER as n, HOSTED_VOICES as r, hostedLocaleFor as i } from "./hostedVoiceCatalogue.js";
|
|
3
|
+
import { buildRequest as a, classifyHttpFailure as o, decodeAudioResponse as s, disableDurationMs as c, shouldDisableEngine as l, speakableChunks as u } from "./hostedTtsProtocol.js";
|
|
4
|
+
import { createPieceSink as d, isOutputRefusal as f } from "./pieceSink.js";
|
|
5
5
|
//#region src/shared/assistant/speech/hostedTtsSpeechEngine.ts
|
|
6
|
-
function
|
|
6
|
+
function p(e) {
|
|
7
7
|
let t = Object.entries(e).map(([e, t]) => `${e.toLowerCase()}:${t}`).sort().join("\n"), n = 5381;
|
|
8
8
|
for (let e = 0; e < t.length; e += 1) n = (n << 5) + n + t.charCodeAt(e) | 0;
|
|
9
9
|
return `${t.length}:${n >>> 0}`;
|
|
10
10
|
}
|
|
11
|
-
var
|
|
11
|
+
var m = new Set([
|
|
12
12
|
429,
|
|
13
13
|
502,
|
|
14
14
|
503,
|
|
15
15
|
504
|
|
16
|
-
]),
|
|
17
|
-
function
|
|
16
|
+
]), h = 3, g = 400, _ = 2e3;
|
|
17
|
+
function v(e, t) {
|
|
18
18
|
let n = e === null ? NaN : Number(e);
|
|
19
|
-
return Math.min(Number.isFinite(n) && n >= 0 ? n * 1e3 :
|
|
19
|
+
return Math.min(Number.isFinite(n) && n >= 0 ? n * 1e3 : g * 2 ** (t - 1), _);
|
|
20
20
|
}
|
|
21
|
-
var
|
|
22
|
-
function
|
|
23
|
-
return
|
|
21
|
+
var y = 6e4, b = 2, x = 1e3;
|
|
22
|
+
function S(e) {
|
|
23
|
+
return y + e / x * 2 * 1e3;
|
|
24
24
|
}
|
|
25
|
-
var
|
|
25
|
+
var C = class extends Error {
|
|
26
26
|
reason = "unknown";
|
|
27
27
|
constructor() {
|
|
28
28
|
super("audio output made no progress"), this.name = "OutputStalledError";
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
|
-
function
|
|
31
|
+
function w(e) {
|
|
32
32
|
let t = 0, n = !1, r = null, i = () => {
|
|
33
33
|
let n = r;
|
|
34
34
|
n !== null && (n.cancel(), n.cancel = e(() => {
|
|
35
|
-
r === n && (r = null, n.fail(new
|
|
36
|
-
},
|
|
35
|
+
r === n && (r = null, n.fail(new C()));
|
|
36
|
+
}, S(t)));
|
|
37
37
|
};
|
|
38
38
|
return {
|
|
39
39
|
handed(e) {
|
|
@@ -64,7 +64,14 @@ function C(e) {
|
|
|
64
64
|
}
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
|
-
|
|
67
|
+
var T = "data:audio/wav;base64,UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA=", E = [], D = 4;
|
|
68
|
+
function O() {
|
|
69
|
+
for (let e of [...E]) e();
|
|
70
|
+
}
|
|
71
|
+
function k() {
|
|
72
|
+
E.length = 0;
|
|
73
|
+
}
|
|
74
|
+
function A(e, t) {
|
|
68
75
|
return new Promise((n) => {
|
|
69
76
|
if (t.aborted) {
|
|
70
77
|
n();
|
|
@@ -79,183 +86,193 @@ function w(e, t) {
|
|
|
79
86
|
t.addEventListener("abort", i, { once: !0 });
|
|
80
87
|
});
|
|
81
88
|
}
|
|
82
|
-
function
|
|
83
|
-
let
|
|
89
|
+
function j(g) {
|
|
90
|
+
let _ = g.fetchImpl ?? ((...e) => fetch(...e)), y = g.now ?? (() => Date.now()), b = g.setTimer ?? ((e, t) => {
|
|
84
91
|
let n = setTimeout(e, t);
|
|
85
92
|
return () => clearTimeout(n);
|
|
86
|
-
}),
|
|
93
|
+
}), x = null, S = null, O = null, k = !1, j = () => {
|
|
94
|
+
if (!(k || typeof Audio != "function")) try {
|
|
95
|
+
let e = O ?? new Audio();
|
|
96
|
+
if (O = e, x === e) return;
|
|
97
|
+
e.muted = !0, e.src = T, Promise.resolve(e.play()).then(() => {
|
|
98
|
+
O === e && x !== e && e.pause();
|
|
99
|
+
}, () => {});
|
|
100
|
+
} catch {}
|
|
101
|
+
};
|
|
102
|
+
E.push(j), E.length > D && E.shift();
|
|
103
|
+
let M = 0, N = null, P = null, F = null, I = 0, L = null, R = null, z = () => {
|
|
87
104
|
try {
|
|
88
|
-
return
|
|
105
|
+
return g.getHeaders?.() ?? {};
|
|
89
106
|
} catch {
|
|
90
107
|
return {};
|
|
91
108
|
}
|
|
92
|
-
},
|
|
93
|
-
for (let e of [...
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
let e =
|
|
109
|
+
}, B = () => y() >= I ? !1 : R === null ? !0 : p(z()) === R, V = /* @__PURE__ */ new Set(), H = () => {
|
|
110
|
+
for (let e of [...V]) e();
|
|
111
|
+
}, U = () => {
|
|
112
|
+
L !== null && clearTimeout(L), L = null;
|
|
113
|
+
}, W = (e) => {
|
|
114
|
+
U(), L = setTimeout(() => {
|
|
115
|
+
L = null;
|
|
116
|
+
let e = I - y();
|
|
100
117
|
if (e > 0) {
|
|
101
|
-
|
|
118
|
+
W(e);
|
|
102
119
|
return;
|
|
103
120
|
}
|
|
104
|
-
|
|
121
|
+
H();
|
|
105
122
|
}, e);
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
},
|
|
109
|
-
|
|
110
|
-
},
|
|
111
|
-
if (
|
|
112
|
-
|
|
123
|
+
}, G = (e, t) => {
|
|
124
|
+
I = y() + e, R = t, W(e), H();
|
|
125
|
+
}, K = () => {
|
|
126
|
+
S &&= (URL.revokeObjectURL(S), null);
|
|
127
|
+
}, q = () => {
|
|
128
|
+
if (x) {
|
|
129
|
+
x.onended = null, x.onerror = null;
|
|
113
130
|
try {
|
|
114
|
-
|
|
131
|
+
x.pause();
|
|
115
132
|
} catch {}
|
|
116
|
-
|
|
133
|
+
x = null;
|
|
117
134
|
}
|
|
118
|
-
|
|
135
|
+
K();
|
|
119
136
|
};
|
|
120
|
-
async function
|
|
121
|
-
let c =
|
|
122
|
-
for (let e = 1; u = await g
|
|
137
|
+
async function J(e, t, r) {
|
|
138
|
+
let c = a(e, i(t.lang) ?? t.lang, n, t.voice?.name ?? null, t.rate), l = g.getHeaders?.() ?? {}, u;
|
|
139
|
+
for (let e = 1; u = await _(g.endpoint, {
|
|
123
140
|
method: "POST",
|
|
124
|
-
signal:
|
|
141
|
+
signal: r,
|
|
125
142
|
headers: {
|
|
126
143
|
"content-type": "application/json",
|
|
127
144
|
...l
|
|
128
145
|
},
|
|
129
146
|
body: JSON.stringify(c)
|
|
130
147
|
}), !u.ok; e += 1) {
|
|
131
|
-
if (!(
|
|
148
|
+
if (!(m.has(u.status) && e < h && !r.aborted)) {
|
|
132
149
|
let e = /* @__PURE__ */ Error(`hosted tts ${u.status}`);
|
|
133
|
-
throw e.reason =
|
|
150
|
+
throw e.reason = o(u.status), e.headers = p(l), e;
|
|
134
151
|
}
|
|
135
|
-
await
|
|
152
|
+
await A(v(u.headers.get("retry-after"), e), r);
|
|
136
153
|
}
|
|
137
|
-
let d = u.headers.get("content-type"),
|
|
138
|
-
if (!
|
|
154
|
+
let d = u.headers.get("content-type"), f = s(d, d?.includes("json") ? await u.json() : await u.arrayBuffer());
|
|
155
|
+
if (!f) {
|
|
139
156
|
let e = /* @__PURE__ */ Error("hosted tts returned no audio");
|
|
140
157
|
throw e.reason = "unknown", e;
|
|
141
158
|
}
|
|
142
|
-
return
|
|
159
|
+
return f;
|
|
143
160
|
}
|
|
144
|
-
function
|
|
145
|
-
return new Promise((
|
|
146
|
-
if (
|
|
147
|
-
|
|
161
|
+
function Y(n, r, i) {
|
|
162
|
+
return new Promise((a, o) => {
|
|
163
|
+
if (r !== M) {
|
|
164
|
+
a();
|
|
148
165
|
return;
|
|
149
166
|
}
|
|
150
|
-
let
|
|
151
|
-
|
|
152
|
-
let
|
|
153
|
-
e(
|
|
154
|
-
|
|
155
|
-
},
|
|
156
|
-
if (
|
|
157
|
-
|
|
167
|
+
let s = new Blob([n.slice().buffer], { type: "audio/mpeg" });
|
|
168
|
+
K(), S = URL.createObjectURL(s);
|
|
169
|
+
let c;
|
|
170
|
+
e() ? (c = new Audio(S), t(c)) : (c = O ?? new Audio(), O = c, c.muted = !1, c.src = S), x = c, c.onended = () => {
|
|
171
|
+
r === M && a();
|
|
172
|
+
}, c.onerror = () => {
|
|
173
|
+
if (r !== M) {
|
|
174
|
+
a();
|
|
158
175
|
return;
|
|
159
176
|
}
|
|
160
177
|
let e = /* @__PURE__ */ Error("audio playback failed");
|
|
161
|
-
e.reason = "unknown",
|
|
162
|
-
},
|
|
163
|
-
|
|
178
|
+
e.reason = "unknown", o(e);
|
|
179
|
+
}, c.play().then(() => {
|
|
180
|
+
c === O && !c.muted && (k = !0), r === M && i?.();
|
|
164
181
|
}, (e) => {
|
|
165
|
-
if (
|
|
166
|
-
|
|
182
|
+
if (r !== M) {
|
|
183
|
+
a();
|
|
167
184
|
return;
|
|
168
185
|
}
|
|
169
|
-
let t = /* @__PURE__ */ Error("audio play rejected"),
|
|
170
|
-
t.reason =
|
|
186
|
+
let t = /* @__PURE__ */ Error("audio play rejected"), n = e?.name === "NotAllowedError";
|
|
187
|
+
t.reason = n ? "not-allowed" : "unknown", t.transient = n, o(t);
|
|
171
188
|
});
|
|
172
189
|
});
|
|
173
190
|
}
|
|
174
191
|
return {
|
|
175
192
|
id: "hosted-tts",
|
|
176
193
|
isSupported() {
|
|
177
|
-
return
|
|
194
|
+
return B() ? !1 : typeof window < "u" && typeof window.Audio == "function" && typeof fetch == "function";
|
|
178
195
|
},
|
|
179
196
|
listVoices() {
|
|
180
|
-
return
|
|
197
|
+
return B() ? [] : [...r];
|
|
181
198
|
},
|
|
182
199
|
subscribeVoices(e) {
|
|
183
|
-
return
|
|
184
|
-
|
|
200
|
+
return V.add(e), () => {
|
|
201
|
+
V.delete(e);
|
|
185
202
|
};
|
|
186
203
|
},
|
|
187
204
|
speak(e, t, n = {}) {
|
|
188
|
-
let r =
|
|
205
|
+
let r = u(e);
|
|
189
206
|
if (r.length === 0) {
|
|
190
207
|
n.onEnd?.();
|
|
191
208
|
return;
|
|
192
209
|
}
|
|
193
|
-
|
|
194
|
-
let i =
|
|
195
|
-
|
|
210
|
+
M += 1;
|
|
211
|
+
let i = M;
|
|
212
|
+
q(), P?.cancel(), F?.stop(), j();
|
|
196
213
|
let a = new AbortController();
|
|
197
|
-
|
|
198
|
-
let o = !1,
|
|
214
|
+
N = a;
|
|
215
|
+
let o = !1, s = () => {
|
|
199
216
|
o || (o = !0, n.onStart?.());
|
|
200
217
|
}, p = (e) => {
|
|
201
|
-
let n =
|
|
218
|
+
let n = J(e, t, a.signal);
|
|
202
219
|
return n.catch(() => {}), n;
|
|
203
|
-
}, m = t.audioOutput ?
|
|
204
|
-
play: (e) =>
|
|
205
|
-
isCurrent: () => i ===
|
|
220
|
+
}, m = t.audioOutput ? w(b) : null, h = t.audioOutput ? d(t.audioOutput, {
|
|
221
|
+
play: (e) => Y(e, i, s),
|
|
222
|
+
isCurrent: () => i === M,
|
|
206
223
|
onChunkStart: (e) => {
|
|
207
224
|
m?.progress(), n.onChunkStart?.(e);
|
|
208
225
|
},
|
|
209
|
-
onStart:
|
|
226
|
+
onStart: s
|
|
210
227
|
}) : null;
|
|
211
|
-
|
|
228
|
+
P = h, F = m;
|
|
212
229
|
let g = (e) => m ? m.guard(e) : e, _ = () => {
|
|
213
|
-
h && (m?.stop(),
|
|
230
|
+
h && (m?.stop(), F === m && (F = null), P === h && (P = null));
|
|
214
231
|
};
|
|
215
232
|
(async () => {
|
|
216
233
|
try {
|
|
217
234
|
let e = p(r[0]);
|
|
218
235
|
for (let t = 0; t < r.length; t += 1) {
|
|
219
|
-
if (i !==
|
|
236
|
+
if (i !== M) return;
|
|
220
237
|
let a = await e;
|
|
221
|
-
if (i !==
|
|
238
|
+
if (i !== M) return;
|
|
222
239
|
let o = r[t + 1];
|
|
223
240
|
if (o !== void 0 && (e = p(o)), h) {
|
|
224
241
|
m?.handed(a.byteLength), await g(h.write(a));
|
|
225
242
|
continue;
|
|
226
243
|
}
|
|
227
|
-
n.onChunkStart?.(t), await
|
|
244
|
+
n.onChunkStart?.(t), await Y(a, i, s);
|
|
228
245
|
}
|
|
229
246
|
if (h) {
|
|
230
247
|
let e = await g(h.drain());
|
|
231
|
-
if (i !==
|
|
248
|
+
if (i !== M) return;
|
|
232
249
|
if (!e) throw Object.assign(/* @__PURE__ */ Error("audio output did not finish"), { reason: "unknown" });
|
|
233
250
|
}
|
|
234
|
-
i ===
|
|
251
|
+
i === M && (_(), n.onEnd?.());
|
|
235
252
|
} catch (e) {
|
|
236
|
-
if (a.abort(), i !==
|
|
253
|
+
if (a.abort(), i !== M) return;
|
|
237
254
|
let t = null;
|
|
238
255
|
if (h) {
|
|
239
|
-
let n = e instanceof
|
|
240
|
-
if (i !==
|
|
241
|
-
h.cancel(),
|
|
256
|
+
let n = e instanceof C ? null : await g(h.drain()).then(() => null, (e) => e);
|
|
257
|
+
if (i !== M) return;
|
|
258
|
+
h.cancel(), f(e) ? t = e : f(n) && (t = n);
|
|
242
259
|
}
|
|
243
|
-
if (
|
|
260
|
+
if (M += 1, q(), _(), t) {
|
|
244
261
|
n.onError?.("output-not-cleared");
|
|
245
262
|
return;
|
|
246
263
|
}
|
|
247
264
|
let r = e?.reason;
|
|
248
|
-
|
|
265
|
+
l(r, e?.transient === !0) && G(c(r), r === "not-allowed" ? e.headers ?? null : null), n.onError?.(r === "not-allowed" || r === "unsupported" || r === "cancelled" ? r : "unknown");
|
|
249
266
|
}
|
|
250
267
|
})();
|
|
251
268
|
},
|
|
252
269
|
headersChanged() {
|
|
253
|
-
|
|
270
|
+
y() >= I || R === null || B() || (I = 0, R = null, U(), H());
|
|
254
271
|
},
|
|
255
272
|
cancel() {
|
|
256
|
-
|
|
273
|
+
M += 1, N?.abort(), N = null, q(), P?.cancel(), P = null, F?.stop(), F = null;
|
|
257
274
|
}
|
|
258
275
|
};
|
|
259
276
|
}
|
|
260
277
|
//#endregion
|
|
261
|
-
export {
|
|
278
|
+
export { x as HOSTED_OUTPUT_MIN_AUDIO_BYTES_PER_SECOND, b as HOSTED_OUTPUT_STALL_AUDIO_RATIO, y as HOSTED_OUTPUT_STALL_BASE_MS, T as SILENT_WAV_DATA_URI, j as createHostedTtsEngine, S as hostedOutputStallMs, O as primeHostedPlayback, k as resetHostedPlaybackForTest, v as retryDelayMs };
|
|
@@ -15,23 +15,26 @@ function r(e) {
|
|
|
15
15
|
}
|
|
16
16
|
function i({ device: i, hosted: a }) {
|
|
17
17
|
if (!a) return i;
|
|
18
|
-
let o = new Set(a.listVoices().map((e) => e.name)), s = (e) => e.source === "hosted" || o.has(e.name), c = (e, t, r) => {
|
|
18
|
+
let o = new Set(a.listVoices().map((e) => e.name)), s = (e) => e.source === "hosted" || o.has(e.name), c = (e, t, r, a = !1) => {
|
|
19
|
+
let o = () => {
|
|
20
|
+
a && r?.onDeviceFallback?.();
|
|
21
|
+
};
|
|
19
22
|
if (!t.voice || !s(t.voice)) {
|
|
20
|
-
i.speak(e, t, r);
|
|
23
|
+
o(), i.speak(e, t, r);
|
|
21
24
|
return;
|
|
22
25
|
}
|
|
23
26
|
if (t.fallback === !1) {
|
|
24
27
|
r?.onError?.("unsupported");
|
|
25
28
|
return;
|
|
26
29
|
}
|
|
27
|
-
let
|
|
28
|
-
if (!
|
|
30
|
+
let c = n(i.listVoices(), t.lang);
|
|
31
|
+
if (!c) {
|
|
29
32
|
r?.onError?.("unsupported");
|
|
30
33
|
return;
|
|
31
34
|
}
|
|
32
|
-
i.speak(e, {
|
|
35
|
+
o(), i.speak(e, {
|
|
33
36
|
...t,
|
|
34
|
-
voice:
|
|
37
|
+
voice: c
|
|
35
38
|
}, r);
|
|
36
39
|
}, l = (t) => a.isSupported() ? t.voice ? o.has(t.voice.name) ? a : i : e(t.lang) ? a : i : i;
|
|
37
40
|
return {
|
|
@@ -68,7 +71,7 @@ function i({ device: i, hosted: a }) {
|
|
|
68
71
|
return;
|
|
69
72
|
}
|
|
70
73
|
if (!s) {
|
|
71
|
-
c(e, n, o);
|
|
74
|
+
c(e, n, o, !0);
|
|
72
75
|
return;
|
|
73
76
|
}
|
|
74
77
|
let a = u >= 0 ? t(e).slice(u + 1) : [];
|
|
@@ -81,7 +84,7 @@ function i({ device: i, hosted: a }) {
|
|
|
81
84
|
...o,
|
|
82
85
|
onStart: void 0,
|
|
83
86
|
onChunkStart: (e) => o?.onChunkStart?.(l + e)
|
|
84
|
-
});
|
|
87
|
+
}, !0);
|
|
85
88
|
}
|
|
86
89
|
});
|
|
87
90
|
},
|
|
@@ -191,6 +191,17 @@ export interface SpeechSpeakHandlers {
|
|
|
191
191
|
/** The whole queue finished on its own. */
|
|
192
192
|
onEnd?: () => void;
|
|
193
193
|
onError?: (reason: SpeechErrorReason) => void;
|
|
194
|
+
/**
|
|
195
|
+
* ★ BOFF-9408: the hosted voice could not play this reply, so the rest of it
|
|
196
|
+
* (or all of it) is about to be spoken on the DEVICE voice.
|
|
197
|
+
*
|
|
198
|
+
* The routing engine calls it once, just before the device voice starts, so
|
|
199
|
+
* the caller can say so on screen instead of the user hearing a different
|
|
200
|
+
* voice with no explanation. The fallback changes nothing the user chose:
|
|
201
|
+
* their saved voice is untouched and the next reply tries the hosted voice
|
|
202
|
+
* again. Optional; an engine with no fallback never calls it.
|
|
203
|
+
*/
|
|
204
|
+
onDeviceFallback?: () => void;
|
|
194
205
|
}
|
|
195
206
|
export interface SpeechEngine {
|
|
196
207
|
/** Stable id, for logging and for telling engines apart in a future swap. */
|