@burdenoff/fe-libs 2026.914.1 → 2026.914.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 (47) hide show
  1. package/dist/shared/assistant/index.d.ts +1 -0
  2. package/dist/shared/assistant/index.d.ts.map +1 -1
  3. package/dist/shared/assistant/speech/browserSpeechEngine.d.ts.map +1 -1
  4. package/dist/shared/assistant/speech/browserSpeechEngine.js +53 -26
  5. package/dist/shared/assistant/speech/createSpeechEngine.d.ts +25 -2
  6. package/dist/shared/assistant/speech/createSpeechEngine.d.ts.map +1 -1
  7. package/dist/shared/assistant/speech/createSpeechEngine.js +36 -13
  8. package/dist/shared/assistant/speech/hostedTtsProtocol.d.ts +25 -1
  9. package/dist/shared/assistant/speech/hostedTtsProtocol.d.ts.map +1 -1
  10. package/dist/shared/assistant/speech/hostedTtsProtocol.js +15 -8
  11. package/dist/shared/assistant/speech/hostedTtsSpeechEngine.d.ts +16 -1
  12. package/dist/shared/assistant/speech/hostedTtsSpeechEngine.d.ts.map +1 -1
  13. package/dist/shared/assistant/speech/hostedTtsSpeechEngine.js +84 -52
  14. package/dist/shared/assistant/speech/hostedVoiceCatalogue.d.ts.map +1 -1
  15. package/dist/shared/assistant/speech/hostedVoiceCatalogue.js +2 -1
  16. package/dist/shared/assistant/speech/index.d.ts +1 -1
  17. package/dist/shared/assistant/speech/index.d.ts.map +1 -1
  18. package/dist/shared/assistant/speech/routingSpeechEngine.d.ts.map +1 -1
  19. package/dist/shared/assistant/speech/routingSpeechEngine.js +41 -20
  20. package/dist/shared/assistant/speech/types.d.ts +10 -0
  21. package/dist/shared/assistant/speech/types.d.ts.map +1 -1
  22. package/dist/shared/assistant/turnActivity.d.ts +5 -1
  23. package/dist/shared/assistant/turnActivity.d.ts.map +1 -1
  24. package/dist/shared/assistant/ui/AssistantMessageList.d.ts.map +1 -1
  25. package/dist/shared/assistant/ui/AssistantMessageList.js +28 -27
  26. package/dist/shared/assistant/ui/AssistantSpeechProvider.d.ts.map +1 -1
  27. package/dist/shared/assistant/ui/AssistantSpeechProvider.js +209 -190
  28. package/dist/shared/assistant/ui/AssistantTurnActivity.d.ts +6 -3
  29. package/dist/shared/assistant/ui/AssistantTurnActivity.d.ts.map +1 -1
  30. package/dist/shared/assistant/ui/AssistantTurnActivity.js +8 -8
  31. package/dist/shared/assistant/ui/usePrefersReducedMotion.d.ts +55 -0
  32. package/dist/shared/assistant/ui/usePrefersReducedMotion.d.ts.map +1 -0
  33. package/dist/shared/assistant/ui/usePrefersReducedMotion.js +28 -0
  34. package/dist/shared/assistant/ui/voice/VoiceMiniController.d.ts +4 -2
  35. package/dist/shared/assistant/ui/voice/VoiceMiniController.d.ts.map +1 -1
  36. package/dist/shared/assistant/ui/voice/VoiceStage.d.ts +4 -2
  37. package/dist/shared/assistant/ui/voice/VoiceStage.d.ts.map +1 -1
  38. package/dist/shared/assistant/ui/voice/VoiceStatusText.d.ts +12 -1
  39. package/dist/shared/assistant/ui/voice/VoiceStatusText.d.ts.map +1 -1
  40. package/dist/shared/assistant/ui/voice/VoiceStatusText.js +19 -18
  41. package/dist/shared/assistant/voice/speechVoices.d.ts +15 -0
  42. package/dist/shared/assistant/voice/speechVoices.d.ts.map +1 -1
  43. package/dist/shared/assistant/voice/speechVoices.js +31 -26
  44. package/dist/shared/assistant/voice/voiceStatus.d.ts +8 -2
  45. package/dist/shared/assistant/voice/voiceStatus.d.ts.map +1 -1
  46. package/dist/shared-assistant.js +100 -99
  47. package/package.json +1 -1
@@ -1,23 +1,23 @@
1
1
  import { useI18n as e } from "../../providers/shell/I18nProvider.js";
2
2
  import { useAssistantTr as t } from "../i18n.js";
3
3
  import { isVoiceTurn as n } from "../voice/turnVisibility.js";
4
- import { getSpeechEngine as r } from "../speech/createSpeechEngine.js";
5
- import { VOICE_LIST_TIMEOUT_MS as i, languageDisplayName as a, selectVoiceForLocale as o, speechGate as s, speechLangForLocale as c, voiceAvailability as ee, voicesForLocale as l } from "../voice/speechVoices.js";
6
- import { chunkForSpeech as te, toSpeakableText as u } from "../voice/speakableText.js";
7
- import { voicePreviewText as d } from "../voice/voicePreview.js";
8
- import { findAssistantLanguage as ne } from "../voice/languages.js";
9
- import { resolveLanguagePair as re } from "../voice/languagePair.js";
10
- import { speechLabelsFor as ie } from "../voice/speechLabels.js";
11
- import { resolveSpeechLocale as ae } from "../voice/replyLanguage.js";
12
- import { IDLE_PLAYBACK as oe, isSpeakingMessage as f, publishedChunks as se, speechPlaybackReducer as ce } from "../voice/speechPlayback.js";
13
- import { useAssistantStore as p } from "../store.js";
4
+ import { VOICE_LIST_TIMEOUT_MS as r, languageDisplayName as i, selectVoiceForLocale as a, speechGate as o, speechLangForLocale as s, voiceAvailability as c, voicesForLocale as l } from "../voice/speechVoices.js";
5
+ import { getSpeechEngine as u, subscribeSpeechEngine as d } from "../speech/createSpeechEngine.js";
6
+ import { chunkForSpeech as ee, toSpeakableText as f } from "../voice/speakableText.js";
7
+ import { voicePreviewText as te } from "../voice/voicePreview.js";
8
+ import { findAssistantLanguage as p } from "../voice/languages.js";
9
+ import { resolveLanguagePair as ne } from "../voice/languagePair.js";
10
+ import { speechLabelsFor as re } from "../voice/speechLabels.js";
11
+ import { resolveSpeechLocale as ie } from "../voice/replyLanguage.js";
12
+ import { IDLE_PLAYBACK as ae, isSpeakingMessage as oe, publishedChunks as se, speechPlaybackReducer as ce } from "../voice/speechPlayback.js";
13
+ import { useAssistantStore as m } from "../store.js";
14
14
  import { isTurnInFlight as le } from "../turnState.js";
15
15
  import { parseToolActivity as ue } from "./AssistantToolActivity.js";
16
16
  import "../speech/index.js";
17
- import { createContext as m, useCallback as h, useContext as g, useEffect as _, useMemo as v, useReducer as de, useRef as y, useState as b } from "react";
18
- import { jsx as fe } from "react/jsx-runtime";
17
+ import { createContext as de, useCallback as h, useContext as g, useEffect as _, useMemo as v, useReducer as fe, useRef as y, useState as b, useSyncExternalStore as pe } from "react";
18
+ import { jsx as me } from "react/jsx-runtime";
19
19
  //#region src/shared/assistant/ui/AssistantSpeechProvider.tsx
20
- var pe = {
20
+ var he = {
21
21
  codeBlock: "",
22
22
  image: "",
23
23
  link: "",
@@ -26,7 +26,7 @@ var pe = {
26
26
  emailAt: "at",
27
27
  emailDot: "dot",
28
28
  nestedItem: ""
29
- }, me = 4e3, x = m({
29
+ }, ge = 4e3, x = de({
30
30
  isSupported: !1,
31
31
  availability: "unsupported",
32
32
  isBlocked: !0,
@@ -51,192 +51,209 @@ var pe = {
51
51
  function S() {
52
52
  return g(x);
53
53
  }
54
- var C = ({ children: m, visible: g = !0, engine: S = r() }) => {
55
- let C = t(), { locale: w } = e(), [T, E] = de(ce, oe), [D, O] = b(() => S.listVoices()), [k, he] = b(!1), [A, j] = b(() => /* @__PURE__ */ new Map()), M = y(0), N = y(null);
54
+ var C = ({ children: de, visible: g = !0, engine: S }) => {
55
+ let C = pe(d, u, u), w = S ?? C, T = t(), { locale: E } = e(), [D, O] = fe(ce, ae), [k, A] = b(() => w.listVoices()), [j, M] = b(!1), [N, P] = b(() => /* @__PURE__ */ new Map()), F = y(0), I = y(null);
56
56
  _(() => () => {
57
- N.current !== null && clearTimeout(N.current);
57
+ I.current !== null && clearTimeout(I.current);
58
58
  }, []);
59
- let [P, F] = b(null), I = y(null), L = y(() => {}), R = h(() => {
60
- I.current?.(), I.current = null;
61
- }, []), z = p((e) => e.autoSpeakEnabled), B = p((e) => e.setAutoSpeak), [V, ge] = b(!1), H = y(!1), U = h((e) => {
62
- H.current = e;
63
- }, []), _e = p((e) => e.activeConversationId), W = v(() => S.isSupported(), [S]);
59
+ let [L, R] = b(null), z = y(null), B = y(() => {}), V = h(() => {
60
+ z.current?.(), z.current = null;
61
+ }, []), H = m((e) => e.autoSpeakEnabled), U = m((e) => e.setAutoSpeak), [W, _e] = b(!1), ve = y(!1), ye = h((e) => {
62
+ ve.current = e;
63
+ }, []), be = m((e) => e.activeConversationId), G = v(() => w.isSupported(), [w]);
64
64
  _(() => {
65
- if (!W) return;
65
+ if (!G) return;
66
66
  let e = () => {
67
- O(S.listVoices()), j((e) => e.size === 0 ? e : /* @__PURE__ */ new Map());
67
+ A(w.listVoices()), P((e) => e.size === 0 ? e : /* @__PURE__ */ new Map());
68
68
  };
69
- return e(), S.subscribeVoices(e);
70
- }, [S, W]);
71
- let ve = p((e) => e.assistantLanguage), ye = p((e) => e.replyLanguage), G = p((e) => e.preferredVoices), be = p((e) => e.setPreferredVoice), K = v(() => re(ve, ye, w).reply, [
72
- ve,
73
- ye,
74
- w
75
- ]), q = K.code, J = h((e) => ne(e)?.tag ?? c(e), []), xe = h((e) => ne(e)?.rate ?? .9, []), Y = v(() => ee(W, D, K.tag, k), [
76
- W,
77
- D,
78
- K,
79
- k
80
- ]), X = v(() => ({
81
- codeBlock: C("speech.codeBlock", "Code block"),
82
- image: C("speech.image", "image"),
83
- link: C("speech.link", "link"),
84
- largeTable: C("speech.largeTable", "Table with {{rows}} rows and {{columns}} columns"),
85
- truncated: C("speech.truncated", "The rest of this message is not read aloud"),
86
- nestedItem: C("speech.nestedItem", "Sub-item {{ordinal}}"),
87
- emailAt: C("speech.emailAt", "at"),
88
- emailDot: C("speech.emailDot", "dot")
89
- }), [C]), Z = h(() => {
90
- R(), S.cancel(), E({ type: "stop" });
91
- }, [R, S]), Se = h((e) => {
92
- let t = S.listVoices().find((t) => t.name === e) ?? null;
93
- t && (Z(), S.speak([d(q)], {
94
- voice: t,
95
- lang: J(q)
96
- }));
97
- }, [
98
- S,
69
+ return e(), w.subscribeVoices(e);
70
+ }, [w, G]);
71
+ let xe = m((e) => e.assistantLanguage), Se = m((e) => e.replyLanguage), K = m((e) => e.preferredVoices), Ce = m((e) => e.setPreferredVoice), q = v(() => ne(xe, Se, E).reply, [
72
+ xe,
73
+ Se,
74
+ E
75
+ ]), J = q.code, Y = h((e) => p(e)?.tag ?? s(e), []), we = h((e) => p(e)?.rate ?? .9, []), X = v(() => c(G, k, q.tag, j), [
76
+ G,
77
+ k,
99
78
  q,
79
+ j
80
+ ]), Te = v(() => ({
81
+ codeBlock: T("speech.codeBlock", "Code block"),
82
+ image: T("speech.image", "image"),
83
+ link: T("speech.link", "link"),
84
+ largeTable: T("speech.largeTable", "Table with {{rows}} rows and {{columns}} columns"),
85
+ truncated: T("speech.truncated", "The rest of this message is not read aloud"),
86
+ nestedItem: T("speech.nestedItem", "Sub-item {{ordinal}}"),
87
+ emailAt: T("speech.emailAt", "at"),
88
+ emailDot: T("speech.emailDot", "dot")
89
+ }), [T]), Z = h(() => {
90
+ V(), w.cancel(), O({ type: "stop" });
91
+ }, [V, w]), Ee = h((e) => {
92
+ let t = w.listVoices().find((t) => t.name === e) ?? null;
93
+ if (t) {
94
+ Z();
95
+ try {
96
+ w.speak([te(J)], {
97
+ voice: t,
98
+ lang: Y(J),
99
+ fallback: !1
100
+ });
101
+ } catch {}
102
+ }
103
+ }, [
104
+ w,
105
+ J,
100
106
  Z,
101
- J
102
- ]), Ce = h((e) => A.get(e) ?? null, [A]), we = h((e, t, n) => {
103
- R();
104
- let r = !1, a = () => {}, o = (i) => {
105
- if (!r) {
106
- if (r = !0, I.current = null, clearTimeout(s), a(), i.length === 0) {
107
- he(!0);
107
+ Y
108
+ ]), De = h((e) => N.get(e) ?? null, [N]), Oe = h((e, t, n) => {
109
+ V();
110
+ let i = !1, a = () => {}, o = (r) => {
111
+ if (!i) {
112
+ if (i = !0, z.current = null, clearTimeout(s), a(), r.length === 0) {
113
+ M(!0);
108
114
  return;
109
115
  }
110
- O(i), L.current(e, t, i, n);
116
+ A(r), B.current(e, t, r, n);
111
117
  }
112
- }, s = setTimeout(() => o(S.listVoices()), i);
113
- a = S.subscribeVoices(() => o(S.listVoices())), I.current = () => {
114
- r || (r = !0, clearTimeout(s), a(), I.current = null);
118
+ }, s = setTimeout(() => o(w.listVoices()), r);
119
+ a = w.subscribeVoices(() => o(w.listVoices())), z.current = () => {
120
+ i || (i = !0, clearTimeout(s), a(), z.current = null);
115
121
  };
116
- }, [R, S]), Q = h((e, t, n, r) => {
117
- let i = n;
118
- if (i.length === 0) {
119
- let e = S.listVoices();
120
- e.length > 0 && (i = e, O(e));
122
+ }, [V, w]), Q = h((e, t, n, r) => {
123
+ let s = n;
124
+ if (s.length === 0) {
125
+ let e = w.listVoices();
126
+ e.length > 0 && (s = e, A(e));
121
127
  }
122
- let c = J(r), l = s(ee(!0, i, r, k));
123
- if (l === "block") {
124
- let t = C("speech.noVoiceForReply", "This reply is in {{language}}, and no {{language}} voice is installed on this device.", { language: a(c, w) });
125
- j((n) => new Map(n).set(e, t));
128
+ let l = Y(r), u = o(c(!0, s, r, j));
129
+ if (u === "block") {
130
+ let t = T("speech.noVoiceForReply", "This reply is in {{language}}, and no {{language}} voice is installed on this device.", { language: i(l, E) });
131
+ P((n) => new Map(n).set(e, t));
126
132
  return;
127
133
  }
128
- if (l === "wait-for-voices") {
129
- we(e, t, r);
134
+ if (u === "wait-for-voices") {
135
+ Oe(e, t, r);
130
136
  return;
131
137
  }
132
- j((t) => {
138
+ P((t) => {
133
139
  if (!t.has(e)) return t;
134
140
  let n = new Map(t);
135
141
  return n.delete(e), n;
136
142
  });
137
- let u = M.current + 1;
138
- M.current = u, E({
143
+ let d = F.current + 1;
144
+ F.current = d, O({
139
145
  type: "start",
140
146
  messageId: e,
141
- requestId: u
147
+ requestId: d
142
148
  });
143
- let d = te(t);
144
- F({
145
- requestId: u,
146
- chunks: d,
149
+ let f = ee(t);
150
+ R({
151
+ requestId: d,
152
+ chunks: f,
147
153
  index: -1
148
- }), N.current !== null && clearTimeout(N.current), N.current = setTimeout(() => {
149
- F((e) => e?.requestId === u && e.index < 0 ? null : e);
150
- }, me), S.speak(d, {
151
- voice: o(i, c, G[r]),
152
- lang: c,
153
- rate: xe(r)
154
- }, {
155
- onChunkStart: (e) => {
156
- M.current === u && N.current !== null && (clearTimeout(N.current), N.current = null), F((t) => t?.requestId === u ? {
157
- ...t,
158
- index: e
159
- } : M.current === u ? {
160
- requestId: u,
161
- chunks: d,
162
- index: e
163
- } : t);
164
- },
165
- onEnd: () => E({
166
- type: "finished",
167
- requestId: u
168
- }),
169
- onError: () => E({
154
+ }), I.current !== null && clearTimeout(I.current), I.current = setTimeout(() => {
155
+ R((e) => e?.requestId === d && e.index < 0 ? null : e);
156
+ }, ge);
157
+ let te = () => {
158
+ F.current === d && I.current !== null && (clearTimeout(I.current), I.current = null), O({
170
159
  type: "failed",
171
- requestId: u
172
- })
173
- });
160
+ requestId: d
161
+ });
162
+ };
163
+ try {
164
+ w.speak(f, {
165
+ voice: a(s, l, K[r]),
166
+ lang: l,
167
+ rate: we(r)
168
+ }, {
169
+ onChunkStart: (e) => {
170
+ F.current === d && I.current !== null && (clearTimeout(I.current), I.current = null), R((t) => t?.requestId === d ? {
171
+ ...t,
172
+ index: e
173
+ } : F.current === d ? {
174
+ requestId: d,
175
+ chunks: f,
176
+ index: e
177
+ } : t);
178
+ },
179
+ onEnd: () => O({
180
+ type: "finished",
181
+ requestId: d
182
+ }),
183
+ onError: () => O({
184
+ type: "failed",
185
+ requestId: d
186
+ })
187
+ });
188
+ } catch {
189
+ te();
190
+ }
174
191
  }, [
175
- S,
176
192
  w,
177
- G,
178
- xe,
179
- J,
180
- C,
181
- k,
182
- we
193
+ E,
194
+ K,
195
+ we,
196
+ Y,
197
+ T,
198
+ j,
199
+ Oe
183
200
  ]);
184
201
  _(() => {
185
- L.current = Q;
202
+ B.current = Q;
186
203
  }, [Q]);
187
204
  let $ = h((e, t, n) => {
188
- if (!W) return;
189
- let r = ae(u(t, { labels: pe }), n ?? q), i = u(t, { labels: ie(r, X) });
190
- i && Q(e, i, D, r);
205
+ if (!G) return;
206
+ let r = ie(f(t, { labels: he }), n ?? J), i = f(t, { labels: re(r, Te) });
207
+ i && Q(e, i, k, r);
191
208
  }, [
192
- W,
193
- X,
194
- q,
209
+ G,
210
+ Te,
211
+ J,
195
212
  Q,
196
- D
197
- ]), Te = h((e, t, n) => {
198
- if (f(T, e)) {
213
+ k
214
+ ]), ke = h((e, t, n) => {
215
+ if (oe(D, e)) {
199
216
  Z();
200
217
  return;
201
218
  }
202
219
  $(e, t, n);
203
220
  }, [
204
- T,
221
+ D,
205
222
  $,
206
223
  Z
207
224
  ]);
208
225
  _(() => {
209
226
  g || Z();
210
227
  }, [g, Z]), _(() => {
211
- Z(), j(/* @__PURE__ */ new Map());
212
- }, [_e, Z]), _(() => {
213
- j((e) => e.size === 0 ? e : /* @__PURE__ */ new Map()), Z();
214
- }, [q, Z]), _(() => () => {
215
- I.current?.(), I.current = null, S.cancel();
216
- }, [S]), _(() => {
228
+ Z(), P(/* @__PURE__ */ new Map());
229
+ }, [be, Z]), _(() => {
230
+ P((e) => e.size === 0 ? e : /* @__PURE__ */ new Map()), Z();
231
+ }, [J, Z]), _(() => () => {
232
+ z.current?.(), z.current = null, w.cancel();
233
+ }, [w]), _(() => {
217
234
  if (typeof window > "u") return;
218
- let e = () => S.cancel();
235
+ let e = () => w.cancel();
219
236
  return window.addEventListener("pagehide", e), () => window.removeEventListener("pagehide", e);
220
- }, [S]), _(() => {
221
- if (typeof document > "u" || !W) return;
237
+ }, [w]), _(() => {
238
+ if (typeof document > "u" || !G) return;
222
239
  let e = () => {
223
240
  if (document.visibilityState !== "visible") return;
224
- let e = S.listVoices();
225
- e.length > 0 && R(), O(e), he(!1), j((e) => e.size === 0 ? e : /* @__PURE__ */ new Map());
241
+ let e = w.listVoices();
242
+ e.length > 0 && V(), A(e), M(!1), P((e) => e.size === 0 ? e : /* @__PURE__ */ new Map());
226
243
  };
227
244
  return document.addEventListener("visibilitychange", e), window.addEventListener("focus", e), () => {
228
245
  document.removeEventListener("visibilitychange", e), window.removeEventListener("focus", e);
229
246
  };
230
247
  }, [
231
- R,
232
- S,
233
- W
248
+ V,
249
+ w,
250
+ G
234
251
  ]);
235
- let Ee = y($);
252
+ let Ae = y($);
236
253
  _(() => {
237
- Ee.current = $;
254
+ Ae.current = $;
238
255
  }, [$]), _(() => {
239
- if (!(!z && !V || !g || !W)) return p.subscribe((e, t) => {
256
+ if (!(!H && !W || !g || !G)) return m.subscribe((e, t) => {
240
257
  let r = e.activeConversationId;
241
258
  if (!r || r !== t.activeConversationId) return;
242
259
  let i = e.processingByConversationId[r], a = t.processingByConversationId[r];
@@ -244,65 +261,67 @@ var C = ({ children: m, visible: g = !0, engine: S = r() }) => {
244
261
  let o = i.messageId ?? a?.messageId ?? null;
245
262
  if (!o) return;
246
263
  let s = e.conversations.find((e) => e.id === r)?.messages.find((e) => e.id === o);
247
- if (!s || s.role !== "assistant" || n(s) !== V || H.current) return;
264
+ if (!s || s.role !== "assistant" || n(s) !== W || ve.current) return;
248
265
  let { text: c } = ue(s.content);
249
- c.trim() && Ee.current(o, c, s.metadata?.lang);
266
+ if (c.trim()) try {
267
+ Ae.current(o, c, s.metadata?.lang);
268
+ } catch {}
250
269
  });
251
270
  }, [
252
- z,
253
- V,
271
+ H,
254
272
  W,
273
+ G,
255
274
  g
256
275
  ]);
257
- let De = v(() => l(D, K.tag), [D, K]), Oe = v(() => o(D, K.tag, G[q]), [
258
- D,
276
+ let je = v(() => l(k, q.tag), [k, q]), Me = v(() => a(k, q.tag, K[J]), [
277
+ k,
278
+ q,
259
279
  K,
260
- G,
261
- q
262
- ]), ke = v(() => ({
263
- isSupported: W,
264
- availability: Y,
265
- isBlocked: Y === "no-voices",
266
- disabledReason: Y === "no-voices" ? C("speech.noVoicesOnDevice", "This device has no speech voices installed. Add one in your device speech or accessibility settings.") : null,
267
- blockedFor: Ce,
268
- voiceChoices: De,
269
- spokenChunks: se(T, P),
270
- activeVoiceName: Oe?.name ?? null,
271
- preferredVoiceName: G[q] ?? null,
272
- setVoice: (e) => be(q, e),
273
- speakingMessageId: T.status === "speaking" ? T.messageId : null,
274
- isSpeaking: (e) => f(T, e),
275
- toggle: Te,
280
+ J
281
+ ]), Ne = v(() => ({
282
+ isSupported: G,
283
+ availability: X,
284
+ isBlocked: X === "no-voices",
285
+ disabledReason: X === "no-voices" ? T("speech.noVoicesOnDevice", "This device has no speech voices installed. Add one in your device speech or accessibility settings.") : null,
286
+ blockedFor: De,
287
+ voiceChoices: je,
288
+ spokenChunks: se(D, L),
289
+ activeVoiceName: Me?.name ?? null,
290
+ preferredVoiceName: K[J] ?? null,
291
+ setVoice: (e) => Ce(J, e),
292
+ speakingMessageId: D.status === "speaking" ? D.messageId : null,
293
+ isSpeaking: (e) => oe(D, e),
294
+ toggle: ke,
276
295
  stop: Z,
277
- autoSpeakEnabled: z,
278
- setAutoSpeak: B,
279
- sessionSpeaking: V,
280
- setSessionSpeaking: ge,
281
- setSessionMicOpen: U,
282
- previewVoice: Se
296
+ autoSpeakEnabled: H,
297
+ setAutoSpeak: U,
298
+ sessionSpeaking: W,
299
+ setSessionSpeaking: _e,
300
+ setSessionMicOpen: ye,
301
+ previewVoice: Ee
283
302
  }), [
284
- W,
285
- Y,
286
- Ce,
287
- De,
288
- Oe,
289
303
  G,
290
- be,
291
- q,
292
- T,
293
- Te,
304
+ X,
305
+ De,
306
+ je,
307
+ Me,
308
+ K,
309
+ Ce,
310
+ J,
311
+ D,
312
+ ke,
294
313
  Z,
295
- z,
296
- B,
297
- V,
314
+ H,
298
315
  U,
299
- Se,
300
- C,
301
- P
316
+ W,
317
+ ye,
318
+ Ee,
319
+ T,
320
+ L
302
321
  ]);
303
- return /* @__PURE__ */ fe(x.Provider, {
304
- value: ke,
305
- children: m
322
+ return /* @__PURE__ */ me(x.Provider, {
323
+ value: Ne,
324
+ children: de
306
325
  });
307
326
  };
308
327
  //#endregion
@@ -27,9 +27,12 @@ export declare const TurnActivityLine: FC<{
27
27
  * stays free of timers, and nothing is written anywhere — the message's content
28
28
  * is untouched while the phrase moves.
29
29
  *
30
- * ★ Under `prefers-reduced-motion` the line holds the step's label instead of
31
- * rotating: text that changes every few seconds on its own is exactly the kind
32
- * of movement that preference asks to stop, as the avatar and caret already do.
30
+ * ★ Under reduced motion the line holds the step's label instead of rotating:
31
+ * text that changes every few seconds on its own is what a person asking for
32
+ * less motion wants stopped, as the avatar and caret already are. BOTH routes
33
+ * count — the OS setting and the app's own "Reduce motion" (BOFF-7284); this
34
+ * used to honour only the first. (For auto-updating text the WCAG criterion is
35
+ * 2.2.2 Pause, Stop, Hide.)
33
36
  */
34
37
  export declare const AssistantTurnActivity: FC<{
35
38
  processing: AssistantConversationProcessing | null | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantTurnActivity.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantTurnActivity.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAGrD,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,iBAAiB,CAAC;AASvE;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,gBAAgB,EAAE,EAAE,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAkBjE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,EAAE,EAAE,CAAC;IACrC,UAAU,EAAE,+BAA+B,GAAG,IAAI,GAAG,SAAS,CAAC;CAChE,CAaA,CAAC"}
1
+ {"version":3,"file":"AssistantTurnActivity.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/AssistantTurnActivity.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAErD,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,iBAAiB,CAAC;AAUvE;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,gBAAgB,EAAE,EAAE,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAkBjE,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qBAAqB,EAAE,EAAE,CAAC;IACrC,UAAU,EAAE,+BAA+B,GAAG,IAAI,GAAG,SAAS,CAAC;CAChE,CAaA,CAAC"}
@@ -1,6 +1,6 @@
1
- import { useMediaQuery as e } from "../../hooks/useMediaQuery.js";
2
- import { useAssistantTr as t } from "../i18n.js";
3
- import { elapsedSince as n, startPhraseTicker as r, turnActivityCopy as i, turnActivityStage as a } from "../turnActivity.js";
1
+ import { useAssistantTr as e } from "../i18n.js";
2
+ import { elapsedSince as t, startPhraseTicker as n, turnActivityCopy as r, turnActivityStage as i } from "../turnActivity.js";
3
+ import { usePrefersReducedMotion as a } from "./usePrefersReducedMotion.js";
4
4
  import { useEffect as o, useState as s } from "react";
5
5
  import { jsx as c, jsxs as l } from "react/jsx-runtime";
6
6
  //#region src/shared/assistant/ui/AssistantTurnActivity.tsx
@@ -16,18 +16,18 @@ var u = ({ shown: e, label: t }) => /* @__PURE__ */ l("p", {
16
16
  "data-testid": "assistant-turn-activity-label",
17
17
  children: t
18
18
  })]
19
- }), d = ({ processing: n }) => {
20
- let r = t(), o = e("(prefers-reduced-motion: reduce)"), s = i(r, n, f(a(n) === "thinking" && !o && typeof n?.stageStartedAt == "number" ? n.stageStartedAt : null));
19
+ }), d = ({ processing: t }) => {
20
+ let n = e(), o = a(), s = r(n, t, f(i(t) === "thinking" && !o && typeof t?.stageStartedAt == "number" ? t.stageStartedAt : null));
21
21
  return s ? /* @__PURE__ */ c(u, {
22
22
  shown: s.shown,
23
23
  label: s.label
24
24
  }) : null;
25
25
  };
26
26
  function f(e) {
27
- let [t, i] = s(() => Date.now());
27
+ let [r, i] = s(() => Date.now());
28
28
  return o(() => {
29
- if (e !== null) return r(e, i);
30
- }, [e]), n(e, t);
29
+ if (e !== null) return n(e, i);
30
+ }, [e]), t(e, r);
31
31
  }
32
32
  //#endregion
33
33
  export { d as AssistantTurnActivity, u as TurnActivityLine };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Whether the person has asked for less motion — by EITHER route.
3
+ *
4
+ * ★★ TWO SIGNALS, and the assistant's own animated text must obey both:
5
+ *
6
+ * - the operating system's `prefers-reduced-motion: reduce`;
7
+ * - the app's own "Reduce motion" setting (ThemePreferences). `ThemeProvider`
8
+ * writes its EFFECTIVE value — a custom theme's override included — to
9
+ * `<html data-reduce-motion>`, and `tokens.css` clamps every CSS animation
10
+ * under that attribute.
11
+ *
12
+ * CSS covers the avatar, the caret and every `motion-safe:` class. It cannot
13
+ * stop a phrase that JavaScript swaps every few seconds, which is why the chat
14
+ * waiting line honoured only the OS setting and the voice status line honoured
15
+ * neither (BOFF-7284). Reading the attribute rather than theme context keeps the
16
+ * JS in lockstep with the CSS by construction, needs no change to
17
+ * `ThemeProvider` (whose context is not exported, and whose `useTheme` throws
18
+ * without a provider), and in a host with no `ThemeProvider` simply falls back
19
+ * to the OS signal.
20
+ */
21
+ export declare const REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
22
+ export declare const REDUCED_MOTION_ATTRIBUTE = "data-reduce-motion";
23
+ /**
24
+ * The current answer, read fresh from the environment every time.
25
+ *
26
+ * ★ Guarded piece by piece — `window`, `matchMedia` and `document` are each
27
+ * absent in some test or render environment here — and never cached: a test
28
+ * may swap `window` between two renders, and a cached `MediaQueryList` would
29
+ * keep answering for the old one.
30
+ */
31
+ export declare function readReducedMotion(): boolean;
32
+ /**
33
+ * Call `onChange` whenever either signal may have changed. Returns the cleanup.
34
+ *
35
+ * ★ The attribute is written by a passive effect in `ThemeProvider`, one commit
36
+ * after its state changes — the observer is what carries that change here
37
+ * without a reload.
38
+ */
39
+ export declare function subscribeReducedMotion(onChange: () => void): () => void;
40
+ /**
41
+ * React binding: re-renders the caller when either signal changes.
42
+ *
43
+ * ★★ The server snapshot is the SAME guarded reader, deliberately — not a
44
+ * constant `false`. On a real server there is no `window`, so it answers false
45
+ * anyway; under a test that stubs the environment it answers from the stub, which
46
+ * is what lets a static render prove the reduced-motion path at all. Both host
47
+ * apps mount with `createRoot`, so there is no hydration for this to mismatch.
48
+ *
49
+ * ★ Not for `hydrateRoot`: on hydration React calls the server snapshot on the
50
+ * client, where this reader can answer true while the server's HTML was rendered
51
+ * with false — a recoverable hydration mismatch. A future SSR consumer must pass
52
+ * a constant server snapshot instead.
53
+ */
54
+ export declare function usePrefersReducedMotion(): boolean;
55
+ //# sourceMappingURL=usePrefersReducedMotion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePrefersReducedMotion.d.ts","sourceRoot":"","sources":["../../../../src/shared/assistant/ui/usePrefersReducedMotion.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,oBAAoB,qCAAqC,CAAC;AACvE,eAAO,MAAM,wBAAwB,uBAAuB,CAAC;AAE7D;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAW3C;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CA+BvE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,IAAI,OAAO,CAMjD"}
@@ -0,0 +1,28 @@
1
+ import { useSyncExternalStore as e } from "react";
2
+ //#region src/shared/assistant/ui/usePrefersReducedMotion.ts
3
+ var t = "(prefers-reduced-motion: reduce)", n = "data-reduce-motion";
4
+ function r() {
5
+ return typeof document < "u" && typeof document.documentElement?.hasAttribute == "function" && document.documentElement.hasAttribute("data-reduce-motion") ? !0 : typeof window > "u" || typeof window.matchMedia != "function" ? !1 : window.matchMedia(t)?.matches === !0;
6
+ }
7
+ function i(e) {
8
+ let r = [];
9
+ if (typeof window < "u" && typeof window.matchMedia == "function") {
10
+ let n = window.matchMedia(t);
11
+ typeof n?.addEventListener == "function" ? (n.addEventListener("change", e), r.push(() => n.removeEventListener("change", e))) : typeof n?.addListener == "function" && (n.addListener(e), r.push(() => n.removeListener(e)));
12
+ }
13
+ if (typeof document < "u" && document.documentElement && typeof MutationObserver < "u") {
14
+ let t = new MutationObserver(e);
15
+ t.observe(document.documentElement, {
16
+ attributes: !0,
17
+ attributeFilter: [n]
18
+ }), r.push(() => t.disconnect());
19
+ }
20
+ return () => {
21
+ for (let e of r) e();
22
+ };
23
+ }
24
+ function a() {
25
+ return e(i, r, r);
26
+ }
27
+ //#endregion
28
+ export { n as REDUCED_MOTION_ATTRIBUTE, t as REDUCED_MOTION_QUERY, r as readReducedMotion, i as subscribeReducedMotion, a as usePrefersReducedMotion };