@burdenoff/fe-libs 2026.1014.4 → 2026.1015.1

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 (46) hide show
  1. package/dist/shared/assistant/avatar/avatarPreferences.d.ts +61 -14
  2. package/dist/shared/assistant/avatar/avatarPreferences.js +54 -21
  3. package/dist/shared/assistant/avatar/live/LiveAvatarTabView.d.ts +33 -0
  4. package/dist/shared/assistant/avatar/live/LiveAvatarTabView.js +278 -0
  5. package/dist/shared/assistant/avatar/live/avatarSessionClient.d.ts +5 -0
  6. package/dist/shared/assistant/avatar/live/avatarSessionClient.js +25 -6
  7. package/dist/shared/assistant/avatar/live/avatarVoice.d.ts +43 -0
  8. package/dist/shared/assistant/avatar/live/avatarVoice.js +45 -0
  9. package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.d.ts +21 -1
  10. package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.js +74 -24
  11. package/dist/shared/assistant/avatar/live/liveAvatarController.d.ts +47 -8
  12. package/dist/shared/assistant/avatar/live/liveAvatarController.js +244 -161
  13. package/dist/shared/assistant/avatar/live/liveAvatarStatus.d.ts +2 -2
  14. package/dist/shared/assistant/avatar/live/liveAvatarStatus.js +11 -4
  15. package/dist/shared/assistant/avatar/useAvatarCopy.d.ts +52 -21
  16. package/dist/shared/assistant/avatar/useAvatarCopy.js +93 -48
  17. package/dist/shared/assistant/i18nManifest.d.ts +24 -2
  18. package/dist/shared/assistant/i18nManifest.js +24 -2
  19. package/dist/shared/assistant/index.d.ts +5 -0
  20. package/dist/shared/assistant/speech/hostedVoiceCatalogue.d.ts +19 -0
  21. package/dist/shared/assistant/speech/hostedVoiceCatalogue.js +25 -18
  22. package/dist/shared/assistant/ui/AIAssistantPanel.js +136 -103
  23. package/dist/shared/assistant/ui/AssistantAvatarTabs.d.ts +10 -0
  24. package/dist/shared/assistant/ui/AssistantAvatarTabs.js +48 -0
  25. package/dist/shared/assistant/ui/AssistantChatArea.js +1260 -1199
  26. package/dist/shared/assistant/ui/assistantLiveAvatarSeam.d.ts +165 -5
  27. package/dist/shared/assistant/ui/assistantLiveAvatarSeam.js +141 -59
  28. package/dist/shared/assistant/ui/avatar/AssistantAvatarTab.d.ts +13 -0
  29. package/dist/shared/assistant/ui/avatar/AssistantAvatarTab.js +70 -0
  30. package/dist/shared/assistant/ui/composer/AssistantComposer.d.ts +18 -0
  31. package/dist/shared/assistant/ui/composer/AssistantComposer.js +121 -102
  32. package/dist/shared/assistant/ui/composer/AssistantHandsFreeInterruptToggle.d.ts +9 -0
  33. package/dist/shared/assistant/ui/composer/AssistantHandsFreeInterruptToggle.js +28 -14
  34. package/dist/shared/assistant/ui/panelOverlays.d.ts +24 -3
  35. package/dist/shared/assistant/ui/panelOverlays.js +11 -10
  36. package/dist/shared/assistant/ui/useVoiceSession.js +234 -233
  37. package/dist/shared/assistant/ui/voice/AssistantVoiceSession.d.ts +6 -0
  38. package/dist/shared/assistant/ui/voice/AssistantVoiceSession.js +161 -149
  39. package/dist/shared/assistant/ui/voice/VoiceMiniController.js +7 -7
  40. package/dist/shared/assistant/ui/voice/VoiceStage.js +7 -7
  41. package/dist/shared/assistant/ui/voice/voiceStartGate.d.ts +57 -0
  42. package/dist/shared/assistant/ui/voice/voiceStartGate.js +9 -0
  43. package/dist/shared/assistant/voice/handsfree/handsFreeSetting.d.ts +7 -1
  44. package/dist/shared/assistant/voice/handsfree/handsFreeSetting.js +2 -2
  45. package/dist/shared-assistant.js +259 -254
  46. package/package.json +1 -1
@@ -1,284 +1,296 @@
1
1
  import { useReturnFocus as e } from "../../../hooks/useReturnFocus.js";
2
2
  import { useAssistantControl as t } from "../../appControls/useAssistantControl.js";
3
3
  import { useAssistantTr as n } from "../../i18n.js";
4
- import { micIsInterrupt as r, showsAssistantCaption as ee, voiceStageState as i } from "../../voice/voiceStageState.js";
5
- import { VOICE_THINKING_LABEL as a, composeVoiceStatus as te, voiceStatusSuffix as ne } from "../../voice/voiceStatus.js";
6
- import { spokenSoFar as o } from "../../voice/voiceSession.js";
7
- import { AssistantAttachmentButton as re } from "../composer/AssistantAttachmentButton.js";
8
- import { AssistantAttachmentChips as ie } from "../composer/AssistantAttachmentChips.js";
9
- import { ASSISTANT_PANEL_ATTR as ae, ASSISTANT_VOICE_SESSION_ATTR as oe } from "../panelOverlays.js";
10
- import { AssistantVoiceOrb as se } from "./AssistantVoiceOrb.js";
11
- import { useLiveAvatarVoiceView as ce } from "../assistantLiveAvatarSeam.js";
12
- import { VoiceMiniController as le } from "./VoiceMiniController.js";
13
- import { VoiceStage as ue } from "./VoiceStage.js";
14
- import { VoiceTranscriptDrawer as de } from "./VoiceTranscriptDrawer.js";
15
- import { escapeBelongsToLayerAbove as fe, voiceSessionEscapeAction as pe } from "./voiceSessionEscape.js";
16
- import { HandsFreePrivacyNotice as me } from "./HandsFreePrivacyNotice.js";
17
- import { useCallback as s, useEffect as c, useMemo as he, useRef as l, useState as u } from "react";
18
- import { Maximize2 as ge, Minimize2 as _e, ScrollText as ve, X as d } from "lucide-react";
19
- import { jsx as f, jsxs as p } from "react/jsx-runtime";
4
+ import { micIsInterrupt as r, showsAssistantCaption as ee, voiceStageState as te } from "../../voice/voiceStageState.js";
5
+ import { VOICE_THINKING_LABEL as i, composeVoiceStatus as ne, voiceStatusSuffix as re } from "../../voice/voiceStatus.js";
6
+ import { spokenSoFar as ie } from "../../voice/voiceSession.js";
7
+ import { useAvatarCopy as ae } from "../../avatar/useAvatarCopy.js";
8
+ import { ASSISTANT_PANEL_ATTR as oe, ASSISTANT_VOICE_SESSION_ATTR as se } from "../panelOverlays.js";
9
+ import { useLiveAvatarHandsFreeGuard as ce, useLiveAvatarTab as le, useLiveAvatarVoiceView as ue } from "../assistantLiveAvatarSeam.js";
10
+ import { AssistantAttachmentButton as de } from "../composer/AssistantAttachmentButton.js";
11
+ import { AssistantAttachmentChips as fe } from "../composer/AssistantAttachmentChips.js";
12
+ import { AssistantVoiceOrb as pe } from "./AssistantVoiceOrb.js";
13
+ import { VoiceMiniController as me } from "./VoiceMiniController.js";
14
+ import { VoiceStage as he } from "./VoiceStage.js";
15
+ import { VoiceTranscriptDrawer as ge } from "./VoiceTranscriptDrawer.js";
16
+ import { escapeBelongsToLayerAbove as _e, voiceSessionEscapeAction as ve } from "./voiceSessionEscape.js";
17
+ import { HandsFreePrivacyNotice as ye } from "./HandsFreePrivacyNotice.js";
18
+ import { useCallback as a, useEffect as o, useMemo as be, useRef as s, useState as c } from "react";
19
+ import { Maximize2 as xe, Minimize2 as Se, ScrollText as Ce, X as l } from "lucide-react";
20
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
20
21
  //#region src/shared/assistant/ui/voice/AssistantVoiceSession.tsx
21
- var m = ({ session: m, turns: be = [], onSaveTranscript: xe, onContinueInChat: Se, notice: g = null, privacyNotice: _ = null, spoken: v, onInterrupt: Ce, onToggleMute: y, attachments: b = [], onAttachFiles: x, onRemoveAttachment: we, onRetryAttachment: Te, canAttachMore: Ee = !0, preparingAttachments: S = 0, attachmentsLocked: C = !1, attachmentNotice: w = null, spokenChunks: T = null, onEnd: E, capturing: De = !0, interrupting: Oe = !1, awaitingEndpoint: ke = !1, holding: D = !1, correcting: Ae = !1, onLetMeFinish: O, onImDone: k, proposalChip: A = null, proposalTray: j = null, renderAfterTurn: je }) => {
22
- let M = n(), { muted: N, error: P } = m, F = l(null), I = l(null), [Me, L] = u("mini"), R = ce(Me, m.phase === "ended"), z = R === "full", B = R === "mini", V = R === "mini" ? "panel" : R === "panel" ? "full" : "mini", [H, U] = u(!1);
23
- t("assistant.transcript", he(() => ({ run: () => H ? { status: "already" } : (U(!0), { status: "done" }) }), [H]));
24
- let W = j !== null && !B, G = l(!1), K = l(y);
25
- K.current = y;
26
- let q = s(() => {
27
- m.phase !== "listening" && m.phase !== "thinking" || N || G.current || o(m) || (G.current = !0, K.current());
28
- }, [N, m]), J = s(() => {
22
+ var f = ({ session: f, turns: Te = [], onSaveTranscript: Ee, onContinueInChat: De, notice: m = null, privacyNotice: h = null, spoken: g, onInterrupt: Oe, onToggleMute: _, attachments: v = [], onAttachFiles: y, onRemoveAttachment: ke, onRetryAttachment: Ae, canAttachMore: je = !0, preparingAttachments: Me = 0, attachmentsLocked: b = !1, attachmentNotice: x = null, spokenChunks: S = null, onEnd: C, capturing: Ne = !0, interrupting: Pe = !1, awaitingEndpoint: Fe = !1, holding: w = !1, correcting: Ie = !1, onLetMeFinish: T, onImDone: E, proposalChip: D = null, proposalTray: O = null, renderAfterTurn: Le, onViewChange: k }) => {
23
+ let A = n(), { muted: j, error: M } = f, N = s(null), P = s(null), [F, I] = c("mini"), L = le().open, [Re, ze] = c(L);
24
+ Re !== L && (ze(L), L && F !== "mini" && I("mini"));
25
+ let R = ue(F, f.phase === "ended"), Be = ce();
26
+ o(() => {
27
+ k?.(R);
28
+ }, [k, R]);
29
+ let Ve = ae(), z = R === "full", B = R === "mini", V = R === "mini" ? "panel" : R === "panel" ? "full" : "mini", [H, U] = c(!1);
30
+ t("assistant.transcript", be(() => ({ run: () => H ? { status: "already" } : (U(!0), { status: "done" }) }), [H]));
31
+ let W = O !== null && !B, G = s(!1), K = s(_);
32
+ K.current = _;
33
+ let q = a(() => {
34
+ f.phase !== "listening" && f.phase !== "thinking" || j || G.current || ie(f) || (G.current = !0, K.current());
35
+ }, [j, f]), J = a(() => {
29
36
  G.current && (G.current = !1, K.current());
30
- }, []), Ne = q;
31
- c(() => {
37
+ }, []), He = q;
38
+ o(() => {
32
39
  let e = () => {
33
40
  document.visibilityState === "hidden" ? q() : J();
34
41
  };
35
42
  return document.addEventListener("visibilitychange", e), () => document.removeEventListener("visibilitychange", e);
36
- }, [q, J]), c(() => {
43
+ }, [q, J]), o(() => {
37
44
  let e = J;
38
45
  return window.addEventListener("focus", e), () => {
39
46
  window.removeEventListener("focus", e), G.current = !1;
40
47
  };
41
- }, []), c(() => {
42
- document.activeElement?.closest("[data-hands-free-notice]") || F.current?.focus();
43
- }, []), e(I, { fallbackSelector: `[${ae}]` }), c(() => {
48
+ }, []), o(() => {
49
+ document.activeElement?.closest("[data-hands-free-notice]") || N.current?.focus();
50
+ }, []), e(P, { fallbackSelector: `[${oe}]` }), o(() => {
44
51
  let e = (e) => {
45
52
  if (e.key === "Escape") {
46
- let t = pe({
53
+ let t = ve({
47
54
  defaultPrevented: e.defaultPrevented,
48
- layerAbove: fe({
55
+ layerAbove: _e({
49
56
  target: e.target,
50
- session: I.current,
57
+ session: P.current,
51
58
  doc: document
52
59
  }),
53
60
  transcriptOpen: H
54
61
  });
55
62
  if (t === "ignore") return;
56
- e.preventDefault(), t === "close-transcript" ? U(!1) : _ ? _.decline() : W ? j?.onClose() : E();
63
+ e.preventDefault(), t === "close-transcript" ? U(!1) : h ? h.decline() : W ? O?.onClose() : C();
57
64
  return;
58
65
  }
59
66
  if (e.key !== "Tab" || !z) return;
60
- let t = (H ? I.current?.querySelector("[data-testid=\"assistant-voice-transcript\"]") : I.current)?.querySelectorAll("button:not([disabled])");
67
+ let t = (H ? P.current?.querySelector("[data-testid=\"assistant-voice-transcript\"]") : P.current)?.querySelectorAll("button:not([disabled])");
61
68
  if (!t || t.length === 0) return;
62
69
  let n = t[0], r = t[t.length - 1];
63
70
  !n || !r || (e.shiftKey && document.activeElement === n ? (e.preventDefault(), r.focus()) : !e.shiftKey && document.activeElement === r && (e.preventDefault(), n.focus()));
64
71
  };
65
72
  return window.addEventListener("keydown", e), () => window.removeEventListener("keydown", e);
66
73
  }, [
67
- E,
74
+ C,
68
75
  z,
69
76
  H,
70
77
  W,
71
- j,
72
- _
78
+ O,
79
+ h
73
80
  ]);
74
- let Y = g !== null && P === null, X = o(m), Z = O && k && m.phase === "listening" && !N && P === null ? {
75
- letMeFinish: O,
76
- imDone: k
77
- } : null, Pe = Z !== null;
78
- c(() => {
79
- if (Pe) return;
81
+ let Y = m !== null && M === null, X = Be && m === null && M === null, Ue = ie(f), Z = T && E && f.phase === "listening" && !j && M === null ? {
82
+ letMeFinish: T,
83
+ imDone: E
84
+ } : null, We = Z !== null;
85
+ o(() => {
86
+ if (We) return;
80
87
  let e = document.activeElement;
81
- (!e || e === document.body) && F.current?.focus();
82
- }, [Pe]);
83
- let Q = i({
84
- snapshot: m,
85
- capturing: De,
86
- interrupting: Oe,
87
- awaitingEndpoint: ke
88
- }), Fe = (() => {
88
+ (!e || e === document.body) && N.current?.focus();
89
+ }, [We]);
90
+ let Q = te({
91
+ snapshot: f,
92
+ capturing: Ne,
93
+ interrupting: Pe,
94
+ awaitingEndpoint: Fe
95
+ }), Ge = (() => {
89
96
  switch (Q) {
90
- case "connecting": return M("voiceSession.connecting", "Starting the microphone…");
91
- case "endpoint-grace": return M("voiceSession.endpointGrace", "Still listening — take your time");
92
- case "interrupting": return M("voiceSession.interrupting", "Stopped — go ahead");
93
- case "thinking": return M(a.key, a.fallback);
94
- case "speaking": return M("voiceSession.speaking", "Speaking — tap to stop");
97
+ case "connecting": return A("voiceSession.connecting", "Starting the microphone…");
98
+ case "endpoint-grace": return A("voiceSession.endpointGrace", "Still listening — take your time");
99
+ case "interrupting": return A("voiceSession.interrupting", "Stopped — go ahead");
100
+ case "thinking": return A(i.key, i.fallback);
101
+ case "speaking": return A("voiceSession.speaking", "Speaking — tap to stop");
95
102
  case "mic-error":
96
103
  case "ended": return "";
97
- case "listening": return N ? M("voiceSession.muted", "Microphone off") : M("voiceSession.listening", "Listening…");
104
+ case "listening": return j ? A("voiceSession.muted", "Microphone off") : A("voiceSession.listening", "Listening…");
98
105
  default: return Q;
99
106
  }
100
- })(), Ie = !N && (Q === "listening" || Q === "endpoint-grace"), Le = Ie && D ? M("voiceSession.holding", "Take your time — tap “I’m done” when you finish") : Ie && Ae ? M("voiceSession.correcting", "Stopped that answer — listening to your correction") : Fe, $ = ne({
101
- tr: M,
107
+ })(), Ke = !j && (Q === "listening" || Q === "endpoint-grace"), qe = Ke && w ? A("voiceSession.holding", "Take your time — tap “I’m done” when you finish") : Ke && Ie ? A("voiceSession.correcting", "Stopped that answer — listening to your correction") : Ge, $ = re({
108
+ tr: A,
102
109
  stage: Q,
103
- muted: N,
104
- label: Le
105
- }), Re = te(Le, $), ze = Q === "thinking";
106
- return /* @__PURE__ */ p("div", {
107
- ref: I,
110
+ muted: j,
111
+ label: qe
112
+ }), Je = ne(qe, $), Ye = Q === "thinking";
113
+ return /* @__PURE__ */ d("div", {
114
+ ref: P,
108
115
  className: `flex flex-col items-center justify-between bg-bg-sunken ${R === "full" ? "fixed inset-x-0 bottom-0 top-14 z-[60] px-6 py-8" : R === "panel" ? "absolute inset-0 z-20 px-4 py-6" : "absolute inset-x-0 bottom-0 z-20 min-h-[11.25rem] border-t border-border-subtle px-3 py-3 shadow-lg"}`,
109
116
  role: "dialog",
110
117
  "aria-modal": z || void 0,
111
- "aria-label": M("voiceSession.title", "Voice conversation"),
118
+ "aria-label": A("voiceSession.title", "Voice conversation"),
112
119
  "data-testid": "assistant-voice-session",
113
- [oe]: "",
120
+ [se]: "",
114
121
  children: [
115
- /* @__PURE__ */ p("button", {
116
- ref: F,
122
+ /* @__PURE__ */ d("button", {
123
+ ref: N,
117
124
  type: "button",
118
- onClick: E,
125
+ onClick: C,
119
126
  className: `absolute ${z ? "right-6 top-6 h-14 gap-2 px-5 text-base" : B ? "right-12 top-2 h-8 gap-1.5 px-2.5 text-xs" : "right-3 top-3 h-11 gap-2 px-4 text-sm"} flex items-center justify-center rounded-full border border-border-default bg-bg-elevated text-text-secondary hover:bg-bg-sunken transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong`,
120
- title: M("voiceSession.end", "End the voice conversation"),
127
+ title: A("voiceSession.end", "End the voice conversation"),
121
128
  "data-testid": "assistant-voice-end",
122
- children: [/* @__PURE__ */ f(d, {
129
+ children: [/* @__PURE__ */ u(l, {
123
130
  className: z ? "size-6" : B ? "size-4" : "size-5",
124
131
  "aria-hidden": "true"
125
- }), /* @__PURE__ */ f("span", {
132
+ }), /* @__PURE__ */ u("span", {
126
133
  className: `truncate whitespace-nowrap font-medium ${B ? "max-w-[3.5rem] text-xs" : "max-w-[7rem]"}`,
127
- children: M("voiceSession.endShort", "End")
134
+ children: A("voiceSession.endShort", "End")
128
135
  })]
129
136
  }),
130
- /* @__PURE__ */ p("div", {
131
- className: `flex flex-col items-center ${B ? `px-3 ${Y || w ? "pt-9 pb-2" : ""}` : `min-h-[1.5rem] ${Y || w ? z ? "pt-14" : "pt-10" : ""}`}`,
137
+ /* @__PURE__ */ d("div", {
138
+ className: `flex flex-col items-center ${B ? `px-3 ${Y || X || x ? "pt-9 pb-2" : ""}` : `min-h-[1.5rem] ${Y || X || x ? z ? "pt-14" : "pt-10" : ""}`}`,
132
139
  children: [
133
- Y ? /* @__PURE__ */ f("p", {
140
+ Y ? /* @__PURE__ */ u("p", {
134
141
  className: "max-w-xs text-center text-xs text-status-warning",
135
142
  "data-testid": "assistant-voice-notice",
136
- children: h(g, M)
143
+ children: p(m, A)
137
144
  }) : null,
138
- /* @__PURE__ */ f("span", {
145
+ X ? /* @__PURE__ */ u("p", {
146
+ className: "max-w-xs text-center text-xs text-text-secondary",
147
+ "data-testid": "assistant-voice-avatar-hands-free",
148
+ children: Ve.handsFreeOff
149
+ }) : null,
150
+ /* @__PURE__ */ u("span", {
139
151
  className: "sr-only start-0 top-0",
140
152
  role: "status",
141
153
  "aria-live": "polite",
142
154
  "aria-atomic": "true",
143
155
  "data-testid": "assistant-voice-notice-announced",
144
- children: Y && g === "reply-not-heard" ? h(g, M) : null
156
+ children: Y && m === "reply-not-heard" ? p(m, A) : X ? Ve.handsFreeOff : null
145
157
  }),
146
- w ? /* @__PURE__ */ f("p", {
158
+ x ? /* @__PURE__ */ u("p", {
147
159
  className: "max-w-xs text-center text-xs text-status-warning",
148
160
  role: "status",
149
161
  "aria-live": "polite",
150
162
  "data-testid": "assistant-voice-attachment-notice",
151
- children: w
163
+ children: x
152
164
  }) : null
153
165
  ]
154
166
  }),
155
167
  (() => {
156
- let e = /* @__PURE__ */ f(se, {
157
- session: m,
168
+ let e = /* @__PURE__ */ u(pe, {
169
+ session: f,
158
170
  compact: B,
159
171
  connecting: Q === "connecting",
160
- onPress: r(Q) ? Ce : y,
161
- label: r(Q) ? M("voiceSession.interrupt", "Stop the assistant and speak") : N ? M("voiceSession.unmute", "Turn the microphone on") : M("voiceSession.mute", "Turn the microphone off")
162
- }), t = Z ? /* @__PURE__ */ f("button", {
172
+ onPress: r(Q) ? Oe : _,
173
+ label: r(Q) ? A("voiceSession.interrupt", "Stop the assistant and speak") : j ? A("voiceSession.unmute", "Turn the microphone on") : A("voiceSession.mute", "Turn the microphone off")
174
+ }), t = Z ? /* @__PURE__ */ u("button", {
163
175
  type: "button",
164
- onClick: D ? Z.imDone : Z.letMeFinish,
165
- className: `flex items-center justify-center rounded-full border font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong ${B ? "h-7 px-3 text-xs" : z ? "h-11 px-6 text-base" : "h-9 px-4 text-sm"} ${D ? "border-transparent bg-action-primary-bg text-action-primary-text hover:opacity-90" : "border-border-default bg-bg-elevated text-text-secondary hover:bg-bg-sunken"}`,
166
- title: D ? M("voiceSession.imDoneHint", "Send what you said now") : M("voiceSession.letMeFinishHint", "Pause sending until you tap “I’m done”"),
176
+ onClick: w ? Z.imDone : Z.letMeFinish,
177
+ className: `flex items-center justify-center rounded-full border font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong ${B ? "h-7 px-3 text-xs" : z ? "h-11 px-6 text-base" : "h-9 px-4 text-sm"} ${w ? "border-transparent bg-action-primary-bg text-action-primary-text hover:opacity-90" : "border-border-default bg-bg-elevated text-text-secondary hover:bg-bg-sunken"}`,
178
+ title: w ? A("voiceSession.imDoneHint", "Send what you said now") : A("voiceSession.letMeFinishHint", "Pause sending until you tap “I’m done”"),
167
179
  "data-testid": "assistant-voice-hold",
168
- "data-holding": D ? "true" : "false",
169
- children: D ? M("voiceSession.imDone", "I’m done") : M("voiceSession.letMeFinish", "Let me finish")
170
- }) : null, n = t ? /* @__PURE__ */ p("div", {
180
+ "data-holding": w ? "true" : "false",
181
+ children: w ? A("voiceSession.imDone", "I’m done") : A("voiceSession.letMeFinish", "Let me finish")
182
+ }) : null, n = t ? /* @__PURE__ */ d("div", {
171
183
  className: "flex flex-col items-center gap-2",
172
184
  children: [e, t]
173
- }) : e, i = ee(Q) ? v : X || null, a = P === null ? null : ye(P, M);
174
- return B ? /* @__PURE__ */ f(le, {
175
- spokenChunks: T,
185
+ }) : e, te = ee(Q) ? g : Ue || null, i = M === null ? null : we(M, A);
186
+ return B ? /* @__PURE__ */ u(me, {
187
+ spokenChunks: S,
176
188
  state: Q,
177
- caption: i,
178
- statusText: Re,
189
+ caption: te,
190
+ statusText: Je,
179
191
  statusSuffix: $,
180
- rotateStatus: ze,
181
- errorText: a,
192
+ rotateStatus: Ye,
193
+ errorText: i,
182
194
  control: n
183
- }) : /* @__PURE__ */ f(ue, {
195
+ }) : /* @__PURE__ */ u(he, {
184
196
  state: Q,
185
- userCaption: X || null,
186
- spokenChunks: T,
187
- assistantCaption: v,
197
+ userCaption: Ue || null,
198
+ spokenChunks: S,
199
+ assistantCaption: g,
188
200
  expanded: z,
189
- statusText: Re,
201
+ statusText: Je,
190
202
  statusSuffix: $,
191
- rotateStatus: ze,
192
- errorText: a,
203
+ rotateStatus: Ye,
204
+ errorText: i,
193
205
  control: n
194
206
  });
195
207
  })(),
196
- A && !W ? /* @__PURE__ */ f("div", {
208
+ D && !W ? /* @__PURE__ */ u("div", {
197
209
  className: B ? "absolute left-20 right-36 top-2 flex justify-center" : `flex w-full justify-center ${z ? "max-w-[calc(100%-12rem)]" : "max-w-[calc(100%-8rem)]"}`,
198
- children: /* @__PURE__ */ f("button", {
210
+ children: /* @__PURE__ */ u("button", {
199
211
  type: "button",
200
212
  onClick: () => {
201
- L("panel"), A.onOpen();
213
+ I("panel"), D.onOpen();
202
214
  },
203
215
  className: `max-w-full truncate rounded-full border border-border-default bg-bg-elevated font-medium text-text-primary transition-colors hover:bg-bg-raised focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong ${B ? "h-8 px-3 text-xs" : "h-10 px-4 text-sm"}`,
204
216
  "data-testid": "assistant-voice-proposal-chip",
205
- children: A.label
217
+ children: D.label
206
218
  })
207
219
  }) : null,
208
- j && W ? /* @__PURE__ */ p("div", {
220
+ O && W ? /* @__PURE__ */ d("div", {
209
221
  className: `absolute z-10 flex flex-col gap-2 overflow-y-auto rounded-lg border border-border-subtle bg-bg-elevated shadow-lg ${z ? "inset-x-6 bottom-24 top-24 mx-auto max-w-xl p-4" : "inset-x-3 bottom-16 top-16 p-3"}`,
210
222
  "data-testid": "assistant-voice-proposal-tray",
211
- children: [/* @__PURE__ */ f("div", {
223
+ children: [/* @__PURE__ */ u("div", {
212
224
  className: "flex justify-end",
213
- children: /* @__PURE__ */ f("button", {
225
+ children: /* @__PURE__ */ u("button", {
214
226
  type: "button",
215
- onClick: j.onClose,
227
+ onClick: O.onClose,
216
228
  className: "flex size-8 items-center justify-center rounded-full text-text-secondary transition-colors hover:bg-bg-raised hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong",
217
- "aria-label": j.closeLabel,
229
+ "aria-label": O.closeLabel,
218
230
  "data-testid": "assistant-voice-proposal-tray-close",
219
- children: /* @__PURE__ */ f(d, {
231
+ children: /* @__PURE__ */ u(l, {
220
232
  className: "size-4",
221
233
  "aria-hidden": "true"
222
234
  })
223
235
  })
224
- }), j.content]
236
+ }), O.content]
225
237
  }) : null,
226
- _ ? /* @__PURE__ */ f(me, {
238
+ h ? /* @__PURE__ */ u(ye, {
227
239
  view: R,
228
- onContinue: _.accept,
229
- onNotNow: _.decline
240
+ onContinue: h.accept,
241
+ onNotNow: h.decline
230
242
  }) : null,
231
- H ? /* @__PURE__ */ f(de, {
232
- turns: be,
243
+ H ? /* @__PURE__ */ u(ge, {
244
+ turns: Te,
233
245
  onClose: () => U(!1),
234
- onSave: xe,
235
- onContinueInChat: Se,
236
- renderAfterTurn: je
246
+ onSave: Ee,
247
+ onContinueInChat: De,
248
+ renderAfterTurn: Le
237
249
  }) : null,
238
- x ? /* @__PURE__ */ f("div", {
250
+ y ? /* @__PURE__ */ u("div", {
239
251
  className: `absolute ${z ? "left-6 top-6" : B ? "left-11 top-2" : "left-3 top-3"}`,
240
- children: /* @__PURE__ */ f(re, {
252
+ children: /* @__PURE__ */ u(de, {
241
253
  onFiles: (e) => {
242
- x(e), L((e) => e === "mini" ? "panel" : e);
254
+ y(e), I((e) => e === "mini" ? "panel" : e);
243
255
  },
244
- onPickerOpen: Ne,
256
+ onPickerOpen: He,
245
257
  direction: "down",
246
- canAddMore: Ee,
247
- disabled: C
258
+ canAddMore: je,
259
+ disabled: b
248
260
  })
249
261
  }) : null,
250
- (b.length > 0 || S > 0) && !B ? /* @__PURE__ */ f("div", {
262
+ (v.length > 0 || Me > 0) && !B ? /* @__PURE__ */ u("div", {
251
263
  className: `absolute ${z ? "bottom-24 left-24 right-24" : "bottom-16 left-16 right-16"}`,
252
264
  "data-testid": "assistant-voice-attachments",
253
- children: /* @__PURE__ */ f(ie, {
254
- attachments: b,
255
- onRemove: we ?? (() => void 0),
256
- onRetry: Te ?? (() => void 0),
257
- preparingCount: S,
258
- disabled: C
265
+ children: /* @__PURE__ */ u(fe, {
266
+ attachments: v,
267
+ onRemove: ke ?? (() => void 0),
268
+ onRetry: Ae ?? (() => void 0),
269
+ preparingCount: Me,
270
+ disabled: b
259
271
  })
260
272
  }) : null,
261
- /* @__PURE__ */ f("button", {
273
+ /* @__PURE__ */ u("button", {
262
274
  type: "button",
263
275
  onClick: () => U((e) => !e),
264
276
  className: `absolute ${z ? "bottom-6 right-6 size-14" : B ? "right-2 top-2 size-8" : "bottom-3 right-3 size-11"} flex items-center justify-center rounded-full bg-bg-elevated text-text-secondary transition-colors hover:bg-bg-raised hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong`,
265
277
  "aria-pressed": H,
266
- "aria-label": M("voiceSession.transcriptTitle", "Voice transcript"),
278
+ "aria-label": A("voiceSession.transcriptTitle", "Voice transcript"),
267
279
  "data-testid": "assistant-voice-transcript-toggle",
268
- children: /* @__PURE__ */ f(ve, { className: z ? "size-6" : B ? "size-4" : "size-5" })
280
+ children: /* @__PURE__ */ u(Ce, { className: z ? "size-6" : B ? "size-4" : "size-5" })
269
281
  }),
270
- /* @__PURE__ */ f("button", {
282
+ /* @__PURE__ */ u("button", {
271
283
  type: "button",
272
- onClick: () => L(V),
284
+ onClick: () => I(V),
273
285
  className: `absolute ${z ? "bottom-6 left-6 size-14" : B ? "left-2 top-2 size-8" : "bottom-3 left-3 size-11"} flex items-center justify-center rounded-full bg-bg-elevated text-text-secondary transition-colors hover:bg-bg-raised hover:text-text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong`,
274
- "aria-label": V === "panel" ? M("voiceSession.expandPanel", "Show the conversation in the panel") : V === "full" ? M("voiceSession.expand", "Expand the conversation to full screen") : M("voiceSession.collapse", "Shrink to the compact controller"),
286
+ "aria-label": V === "panel" ? A("voiceSession.expandPanel", "Show the conversation in the panel") : V === "full" ? A("voiceSession.expand", "Expand the conversation to full screen") : A("voiceSession.collapse", "Shrink to the compact controller"),
275
287
  "data-testid": "assistant-voice-expand",
276
- children: z ? /* @__PURE__ */ f(_e, { className: "size-5" }) : /* @__PURE__ */ f(ge, { className: B ? "size-4" : "size-5" })
288
+ children: z ? /* @__PURE__ */ u(Se, { className: "size-5" }) : /* @__PURE__ */ u(xe, { className: B ? "size-4" : "size-5" })
277
289
  })
278
290
  ]
279
291
  });
280
292
  };
281
- function h(e, t) {
293
+ function p(e, t) {
282
294
  switch (e) {
283
295
  case "network": return t("voiceSession.noticeNetwork", "Speech recognition lost its connection. Check your network — it will retry.");
284
296
  case "not-capturing": return t("voiceSession.noticeNotCapturing", "The microphone did not start. Check that this site is allowed to use it, then try again.");
@@ -290,7 +302,7 @@ function h(e, t) {
290
302
  default: return e;
291
303
  }
292
304
  }
293
- function ye(e, t) {
305
+ function we(e, t) {
294
306
  switch (e) {
295
307
  case "mic-denied": return t("voiceSession.micDenied", "Microphone access is blocked. Allow it in your browser settings to talk.");
296
308
  case "no-speech-support": return t("voiceSession.noSpeechSupport", "This browser cannot listen. Try a different browser, or type instead.");
@@ -299,4 +311,4 @@ function ye(e, t) {
299
311
  }
300
312
  }
301
313
  //#endregion
302
- export { m as AssistantVoiceSession };
314
+ export { f as AssistantVoiceSession };
@@ -1,9 +1,9 @@
1
1
  import { showsAssistantCaption as e, waveformIsActive as t, waveformIsSettled as n } from "../../voice/voiceStageState.js";
2
2
  import { showPreviousChunk as r } from "../../voice/miniCaptionBudget.js";
3
3
  import { isAwaitingFirstChunk as i } from "../../voice/speechPlayback.js";
4
- import { VoiceWaveform as a } from "./VoiceWaveform.js";
5
- import { VoiceStatusText as o } from "./VoiceStatusText.js";
6
- import { LiveAvatarMiniIndicator as s, useLiveAvatarMiniIndicatorShown as c } from "../assistantLiveAvatarSeam.js";
4
+ import { LiveAvatarMiniIndicator as a, useLiveAvatarMiniIndicatorShown as o } from "../assistantLiveAvatarSeam.js";
5
+ import { VoiceWaveform as s } from "./VoiceWaveform.js";
6
+ import { VoiceStatusText as c } from "./VoiceStatusText.js";
7
7
  import { Fragment as l, jsx as u, jsxs as d } from "react/jsx-runtime";
8
8
  //#region src/shared/assistant/ui/voice/VoiceMiniController.tsx
9
9
  var f = " self-stretch justify-end pb-8.5 [container-type:size] [@container_(min-height:4.0312rem)_and_(max-height:5.2343rem)]:[&>p[role=status]]:line-clamp-2 [@container_(max-height:4.0312rem)]:[&>p[role=status]]:line-clamp-1 [@container_(max-height:4.0312rem)]:[&>p[role=status]]:min-h-5 [@container_(max-height:5.2343rem)]:[&>p[role=status]>span:not(:last-child)]:sr-only", p = ({ state: p, caption: m, spokenChunks: h = null, statusText: g, statusSuffix: _, rotateStatus: v, control: y, errorText: b = null }) => {
@@ -14,14 +14,14 @@ var f = " self-stretch justify-end pb-8.5 [container-type:size] [@container_(min
14
14
  "data-state": p,
15
15
  children: [
16
16
  /* @__PURE__ */ d("div", {
17
- className: `flex min-w-0 flex-1 flex-col gap-1.5${c() ? f : ""}`,
17
+ className: `flex min-w-0 flex-1 flex-col gap-1.5${o() ? f : ""}`,
18
18
  children: [/* @__PURE__ */ d("div", {
19
19
  className: "flex items-center gap-2",
20
- children: [/* @__PURE__ */ u(a, {
20
+ children: [/* @__PURE__ */ u(s, {
21
21
  active: t(p),
22
22
  settled: n(p),
23
23
  tone: p === "speaking" ? "speaking" : "listening"
24
- }), /* @__PURE__ */ u(o, {
24
+ }), /* @__PURE__ */ u(c, {
25
25
  className: "truncate text-xs text-text-muted",
26
26
  status: g,
27
27
  suffix: _,
@@ -58,7 +58,7 @@ var f = " self-stretch justify-end pb-8.5 [container-type:size] [@container_(min
58
58
  className: "shrink-0",
59
59
  children: y
60
60
  }),
61
- /* @__PURE__ */ u(s, {})
61
+ /* @__PURE__ */ u(a, {})
62
62
  ]
63
63
  });
64
64
  };
@@ -1,13 +1,13 @@
1
1
  import { showsAssistantCaption as e, showsUserCaption as t, waveformIsActive as n, waveformIsSettled as r } from "../../voice/voiceStageState.js";
2
2
  import { isAwaitingFirstChunk as i } from "../../voice/speechPlayback.js";
3
- import { VoiceCaption as a } from "./VoiceCaption.js";
4
- import { VoiceWaveform as o } from "./VoiceWaveform.js";
5
- import { VoiceStatusText as s } from "./VoiceStatusText.js";
6
- import { useLiveAvatarStage as c } from "../assistantLiveAvatarSeam.js";
3
+ import { useLiveAvatarStage as a } from "../assistantLiveAvatarSeam.js";
4
+ import { VoiceCaption as o } from "./VoiceCaption.js";
5
+ import { VoiceWaveform as s } from "./VoiceWaveform.js";
6
+ import { VoiceStatusText as c } from "./VoiceStatusText.js";
7
7
  import { Fragment as l, jsx as u, jsxs as d } from "react/jsx-runtime";
8
8
  //#region src/shared/assistant/ui/voice/VoiceStage.tsx
9
9
  var f = ({ state: f, userCaption: p, assistantCaption: m, expanded: h, statusText: g, statusSuffix: _, rotateStatus: v, control: y, errorText: b = null, spokenChunks: x = null }) => {
10
- let S = c(), C = e(f) && i(x), w = e(f) ? m : t(f) ? p : null, T = /* @__PURE__ */ u(o, {
10
+ let S = a(), C = e(f) && i(x), w = e(f) ? m : t(f) ? p : null, T = /* @__PURE__ */ u(s, {
11
11
  active: n(f),
12
12
  settled: r(f),
13
13
  tone: f === "speaking" ? "speaking" : "listening",
@@ -22,7 +22,7 @@ var f = ({ state: f, userCaption: p, assistantCaption: m, expanded: h, statusTex
22
22
  "data-testid": "assistant-voice-stage",
23
23
  "data-state": f,
24
24
  children: [
25
- /* @__PURE__ */ u(s, {
25
+ /* @__PURE__ */ u(c, {
26
26
  className: [
27
27
  "text-center text-text-muted",
28
28
  h ? "text-base" : "text-sm",
@@ -46,7 +46,7 @@ var f = ({ state: f, userCaption: p, assistantCaption: m, expanded: h, statusTex
46
46
  }), w ? /* @__PURE__ */ u("div", {
47
47
  className: [h ? "mb-12 max-h-[46vh] w-full overflow-y-auto" : "mb-6 max-h-[38vh] w-full overflow-y-auto", C ? h ? "min-h-[4.875rem]" : "min-h-12" : ""].join(" "),
48
48
  "data-caption-scroll": "",
49
- children: /* @__PURE__ */ u(a, {
49
+ children: /* @__PURE__ */ u(o, {
50
50
  text: w,
51
51
  role: e(f) ? "assistant" : "user",
52
52
  spokenChunks: x,
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Whether a voice conversation can start now, and if not, why (BOFF-9341,
3
+ * critic #6).
4
+ *
5
+ * ★★ ONE check, the composer's FULL one. The composer offers its Voice button
6
+ * only where voice can work at all and the active mode takes it
7
+ * (`onStartVoiceSession`: `voiceSession.available && voiceAllowedInActiveMode`),
8
+ * disables it while the conversation cannot take a turn (its `disabled` and
9
+ * `readOnly`: a turn in flight, an approval being recorded, history loading,
10
+ * navigation busy, a read-only conversation) or while a Send saves its
11
+ * attachments (`committing`), and hides it while dictation holds the
12
+ * microphone. The Avatar tab's "Start a voice conversation" is the same
13
+ * conversation, so it asks exactly this: a button that skipped part of it
14
+ * could open a voice conversation the composer would have refused, or turn on
15
+ * a paid avatar in a mode (the write-capable `api-calls` one with no voice
16
+ * proposal bound to it) where nothing could ever be asked of it.
17
+ *
18
+ * Pure, so the rule is testable without a DOM; the chat area hands it the
19
+ * same expressions it hands the composer (`avatarTab.client.test.tsx` pins
20
+ * that).
21
+ */
22
+ /** Why a voice conversation cannot start now, most lasting first. */
23
+ export type VoiceStartBlocker =
24
+ /** Nothing here can hold a voice conversation (no speech input, or nothing to speak the reply). */
25
+ "unavailable"
26
+ /** The active mode takes no voice (it writes, and no voice proposal is bound to it). */
27
+ | "mode"
28
+ /** This conversation takes no new turn at all. */
29
+ | "read-only"
30
+ /** Not now: a turn in flight, an approval being recorded, history loading, navigation busy. */
31
+ | "busy"
32
+ /** Not now: a Send is saving its attachments. */
33
+ | "committing"
34
+ /** Not now: dictation holds the microphone. */
35
+ | "dictating";
36
+ export interface VoiceStartGateInput {
37
+ /** `voiceSession.available`: speech in and out work here. */
38
+ readonly available: boolean;
39
+ /** `voiceAllowedInActiveMode`. */
40
+ readonly allowedInMode: boolean;
41
+ /** The conversation is read-only (the composer's `readOnly`). */
42
+ readonly readOnly: boolean;
43
+ /** The composer's `disabled`. */
44
+ readonly busy: boolean;
45
+ /** The composer is saving a Send's attachments. */
46
+ readonly committing: boolean;
47
+ /** Composer dictation is listening, or starting to. */
48
+ readonly dictating: boolean;
49
+ }
50
+ /** Null when a voice conversation can start now; else the first reason it cannot. */
51
+ export declare function voiceStartBlocker(input: VoiceStartGateInput): VoiceStartBlocker | null;
52
+ /**
53
+ * A reason nothing the person waits out will lift, here and now: voice cannot
54
+ * work, the mode refuses it, or the conversation takes nothing. The Avatar tab
55
+ * then still offers Turn on, and says that asking needs a voice conversation.
56
+ */
57
+ export declare function voiceStartBlockerLasts(blocker: VoiceStartBlocker | null): boolean;
@@ -0,0 +1,9 @@
1
+ //#region src/shared/assistant/ui/voice/voiceStartGate.ts
2
+ function e(e) {
3
+ return e.available ? e.allowedInMode ? e.readOnly ? "read-only" : e.busy ? "busy" : e.committing ? "committing" : e.dictating ? "dictating" : null : "mode" : "unavailable";
4
+ }
5
+ function t(e) {
6
+ return e === "unavailable" || e === "mode" || e === "read-only";
7
+ }
8
+ //#endregion
9
+ export { e as voiceStartBlocker, t as voiceStartBlockerLasts };