@burdenoff/fe-libs 2026.1014.5 → 2026.1015.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/dist/shared/assistant/avatar/avatarPreferences.d.ts +61 -14
- package/dist/shared/assistant/avatar/avatarPreferences.js +54 -21
- package/dist/shared/assistant/avatar/live/LiveAvatarTabView.d.ts +33 -0
- package/dist/shared/assistant/avatar/live/LiveAvatarTabView.js +278 -0
- package/dist/shared/assistant/avatar/live/avatarSessionClient.d.ts +5 -0
- package/dist/shared/assistant/avatar/live/avatarSessionClient.js +25 -6
- package/dist/shared/assistant/avatar/live/avatarVoice.d.ts +43 -0
- package/dist/shared/assistant/avatar/live/avatarVoice.js +45 -0
- package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.d.ts +21 -1
- package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.js +74 -24
- package/dist/shared/assistant/avatar/live/liveAvatarController.d.ts +47 -8
- package/dist/shared/assistant/avatar/live/liveAvatarController.js +244 -161
- package/dist/shared/assistant/avatar/live/liveAvatarStatus.d.ts +2 -2
- package/dist/shared/assistant/avatar/live/liveAvatarStatus.js +11 -4
- package/dist/shared/assistant/avatar/useAvatarCopy.d.ts +50 -21
- package/dist/shared/assistant/avatar/useAvatarCopy.js +93 -49
- package/dist/shared/assistant/i18nManifest.d.ts +23 -2
- package/dist/shared/assistant/i18nManifest.js +23 -2
- package/dist/shared/assistant/index.d.ts +6 -0
- package/dist/shared/assistant/speech/hostedVoiceCatalogue.d.ts +19 -0
- package/dist/shared/assistant/speech/hostedVoiceCatalogue.js +25 -18
- package/dist/shared/assistant/ui/AIAssistantPanel.js +134 -101
- package/dist/shared/assistant/ui/AssistantAvatarTabs.d.ts +10 -0
- package/dist/shared/assistant/ui/AssistantAvatarTabs.js +48 -0
- package/dist/shared/assistant/ui/AssistantChatArea.js +1261 -1202
- package/dist/shared/assistant/ui/assistantLiveAvatarSeam.d.ts +137 -5
- package/dist/shared/assistant/ui/assistantLiveAvatarSeam.js +114 -55
- package/dist/shared/assistant/ui/avatar/AssistantAvatarTab.d.ts +13 -0
- package/dist/shared/assistant/ui/avatar/AssistantAvatarTab.js +70 -0
- package/dist/shared/assistant/ui/composer/AssistantComposer.d.ts +18 -0
- package/dist/shared/assistant/ui/composer/AssistantComposer.js +121 -102
- package/dist/shared/assistant/ui/panelOverlays.d.ts +24 -3
- package/dist/shared/assistant/ui/panelOverlays.js +11 -10
- package/dist/shared/assistant/ui/useVoiceSession.d.ts +20 -5
- package/dist/shared/assistant/ui/useVoiceSession.js +11 -4
- package/dist/shared/assistant/ui/voice/AssistantVoiceSession.d.ts +8 -2
- package/dist/shared/assistant/ui/voice/AssistantVoiceSession.js +172 -160
- package/dist/shared/assistant/ui/voice/voiceStartGate.d.ts +57 -0
- package/dist/shared/assistant/ui/voice/voiceStartGate.js +9 -0
- package/dist/shared/assistant/voice/voiceReply.d.ts +23 -0
- package/dist/shared/assistant/voice/voiceReply.js +13 -0
- package/dist/shared/assistant/voice/voiceSession.d.ts +10 -0
- package/dist/shared/assistant/voice/voiceSession.js +7 -0
- package/dist/shared-assistant.js +260 -254
- package/package.json +1 -1
|
@@ -6,88 +6,94 @@ import { VOICE_THINKING_LABEL as a, composeVoiceStatus as te, voiceStatusSuffix
|
|
|
6
6
|
import { spokenSoFar as re } from "../../voice/voiceSession.js";
|
|
7
7
|
import { useAvatarCopy as ie } from "../../avatar/useAvatarCopy.js";
|
|
8
8
|
import { ASSISTANT_PANEL_ATTR as ae, ASSISTANT_VOICE_SESSION_ATTR as oe } from "../panelOverlays.js";
|
|
9
|
-
import { useLiveAvatarHandsFreeGuard as se,
|
|
10
|
-
import { AssistantAttachmentButton as
|
|
11
|
-
import { AssistantAttachmentChips as
|
|
12
|
-
import { AssistantVoiceOrb as
|
|
13
|
-
import { VoiceMiniController as
|
|
14
|
-
import { VoiceStage as
|
|
15
|
-
import { VoiceTranscriptDrawer as
|
|
16
|
-
import { escapeBelongsToLayerAbove as
|
|
17
|
-
import { HandsFreePrivacyNotice as
|
|
18
|
-
import { useCallback as
|
|
19
|
-
import { Maximize2 as
|
|
20
|
-
import { jsx as
|
|
9
|
+
import { useLiveAvatarHandsFreeGuard as se, useLiveAvatarTab as ce, useLiveAvatarVoiceView as le } from "../assistantLiveAvatarSeam.js";
|
|
10
|
+
import { AssistantAttachmentButton as ue } from "../composer/AssistantAttachmentButton.js";
|
|
11
|
+
import { AssistantAttachmentChips as de } from "../composer/AssistantAttachmentChips.js";
|
|
12
|
+
import { AssistantVoiceOrb as fe } from "./AssistantVoiceOrb.js";
|
|
13
|
+
import { VoiceMiniController as pe } from "./VoiceMiniController.js";
|
|
14
|
+
import { VoiceStage as me } from "./VoiceStage.js";
|
|
15
|
+
import { VoiceTranscriptDrawer as he } from "./VoiceTranscriptDrawer.js";
|
|
16
|
+
import { escapeBelongsToLayerAbove as ge, voiceSessionEscapeAction as _e } from "./voiceSessionEscape.js";
|
|
17
|
+
import { HandsFreePrivacyNotice as ve } from "./HandsFreePrivacyNotice.js";
|
|
18
|
+
import { useCallback as ye, 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";
|
|
21
21
|
//#region src/shared/assistant/ui/voice/AssistantVoiceSession.tsx
|
|
22
|
-
var
|
|
23
|
-
let A = n(), { muted: j, error: M } =
|
|
24
|
-
|
|
25
|
-
let
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
22
|
+
var f = ({ session: f, turns: m = [], onSaveTranscript: Ee, onContinueInChat: De, notice: h = null, privacyNotice: g = null, spoken: _, onInterrupt: Oe, onToggleMute: v, attachments: y = [], onAttachFiles: ke, onRemoveAttachment: Ae, onRetryAttachment: je, canAttachMore: Me = !0, preparingAttachments: b = 0, attachmentsLocked: Ne = !1, attachmentNotice: x = null, spokenChunks: S = null, onEnd: C, capturing: Pe = !0, interrupting: Fe = !1, awaitingEndpoint: Ie = !1, holding: w = !1, correcting: Le = !1, onLetMeFinish: T, onImDone: E, proposalChip: D = null, proposalTray: O = null, renderAfterTurn: Re, onViewChange: k }) => {
|
|
23
|
+
let A = n(), { muted: j, error: M } = f, N = s(null), P = s(null), [ze, F] = c("mini"), I = ce().open, [Be, Ve] = c(I);
|
|
24
|
+
Be !== I && (Ve(I), I && ze !== "mini" && F("mini"));
|
|
25
|
+
let L = le(ze, f.phase === "ended"), He = se();
|
|
26
|
+
o(() => {
|
|
27
|
+
k?.(L);
|
|
28
|
+
}, [k, L]);
|
|
29
|
+
let R = ie(), z = L === "full", B = L === "mini", V = L === "mini" ? "panel" : L === "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(v);
|
|
32
|
+
K.current = v;
|
|
33
|
+
let q = ye(() => {
|
|
34
|
+
f.phase !== "listening" && f.phase !== "thinking" || j || G.current || re(f) || (G.current = !0, K.current());
|
|
35
|
+
}, [j, f]), J = ye(() => {
|
|
36
|
+
G.current && (G.current = !1, K.current());
|
|
37
|
+
}, []), Ue = q;
|
|
38
|
+
o(() => {
|
|
33
39
|
let e = () => {
|
|
34
|
-
document.visibilityState === "hidden" ?
|
|
40
|
+
document.visibilityState === "hidden" ? q() : J();
|
|
35
41
|
};
|
|
36
42
|
return document.addEventListener("visibilitychange", e), () => document.removeEventListener("visibilitychange", e);
|
|
37
|
-
}, [
|
|
38
|
-
let e =
|
|
43
|
+
}, [q, J]), o(() => {
|
|
44
|
+
let e = J;
|
|
39
45
|
return window.addEventListener("focus", e), () => {
|
|
40
|
-
window.removeEventListener("focus", e),
|
|
46
|
+
window.removeEventListener("focus", e), G.current = !1;
|
|
41
47
|
};
|
|
42
|
-
}, []),
|
|
48
|
+
}, []), o(() => {
|
|
43
49
|
document.activeElement?.closest("[data-hands-free-notice]") || N.current?.focus();
|
|
44
|
-
}, []), e(P, { fallbackSelector: `[${ae}]` }),
|
|
50
|
+
}, []), e(P, { fallbackSelector: `[${ae}]` }), o(() => {
|
|
45
51
|
let e = (e) => {
|
|
46
52
|
if (e.key === "Escape") {
|
|
47
|
-
let t =
|
|
53
|
+
let t = _e({
|
|
48
54
|
defaultPrevented: e.defaultPrevented,
|
|
49
|
-
layerAbove:
|
|
55
|
+
layerAbove: ge({
|
|
50
56
|
target: e.target,
|
|
51
57
|
session: P.current,
|
|
52
58
|
doc: document
|
|
53
59
|
}),
|
|
54
|
-
transcriptOpen:
|
|
60
|
+
transcriptOpen: H
|
|
55
61
|
});
|
|
56
62
|
if (t === "ignore") return;
|
|
57
|
-
e.preventDefault(), t === "close-transcript" ?
|
|
63
|
+
e.preventDefault(), t === "close-transcript" ? U(!1) : g ? g.decline() : W ? O?.onClose() : C();
|
|
58
64
|
return;
|
|
59
65
|
}
|
|
60
|
-
if (e.key !== "Tab" || !
|
|
61
|
-
let t = (
|
|
66
|
+
if (e.key !== "Tab" || !z) return;
|
|
67
|
+
let t = (H ? P.current?.querySelector("[data-testid=\"assistant-voice-transcript\"]") : P.current)?.querySelectorAll("button:not([disabled])");
|
|
62
68
|
if (!t || t.length === 0) return;
|
|
63
69
|
let n = t[0], r = t[t.length - 1];
|
|
64
70
|
!n || !r || (e.shiftKey && document.activeElement === n ? (e.preventDefault(), r.focus()) : !e.shiftKey && document.activeElement === r && (e.preventDefault(), n.focus()));
|
|
65
71
|
};
|
|
66
72
|
return window.addEventListener("keydown", e), () => window.removeEventListener("keydown", e);
|
|
67
73
|
}, [
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
C,
|
|
75
|
+
z,
|
|
76
|
+
H,
|
|
77
|
+
W,
|
|
78
|
+
O,
|
|
79
|
+
g
|
|
74
80
|
]);
|
|
75
|
-
let
|
|
76
|
-
letMeFinish:
|
|
77
|
-
imDone:
|
|
78
|
-
} : null,
|
|
79
|
-
|
|
80
|
-
if (
|
|
81
|
+
let Y = h !== null && M === null, X = He && h === null && M === null, We = re(f), Z = T && E && f.phase === "listening" && !j && M === null ? {
|
|
82
|
+
letMeFinish: T,
|
|
83
|
+
imDone: E
|
|
84
|
+
} : null, Ge = Z !== null;
|
|
85
|
+
o(() => {
|
|
86
|
+
if (Ge) return;
|
|
81
87
|
let e = document.activeElement;
|
|
82
88
|
(!e || e === document.body) && N.current?.focus();
|
|
83
|
-
}, [
|
|
84
|
-
let
|
|
85
|
-
snapshot:
|
|
86
|
-
capturing:
|
|
87
|
-
interrupting:
|
|
88
|
-
awaitingEndpoint:
|
|
89
|
-
}),
|
|
90
|
-
switch (
|
|
89
|
+
}, [Ge]);
|
|
90
|
+
let Q = i({
|
|
91
|
+
snapshot: f,
|
|
92
|
+
capturing: Pe,
|
|
93
|
+
interrupting: Fe,
|
|
94
|
+
awaitingEndpoint: Ie
|
|
95
|
+
}), Ke = (() => {
|
|
96
|
+
switch (Q) {
|
|
91
97
|
case "connecting": return A("voiceSession.connecting", "Starting the microphone…");
|
|
92
98
|
case "endpoint-grace": return A("voiceSession.endpointGrace", "Still listening — take your time");
|
|
93
99
|
case "interrupting": return A("voiceSession.interrupting", "Stopped — go ahead");
|
|
@@ -96,195 +102,201 @@ var p = ({ session: p, turns: we = [], onSaveTranscript: Te, onContinueInChat: E
|
|
|
96
102
|
case "mic-error":
|
|
97
103
|
case "ended": return "";
|
|
98
104
|
case "listening": return j ? A("voiceSession.muted", "Microphone off") : A("voiceSession.listening", "Listening…");
|
|
99
|
-
default: return
|
|
105
|
+
default: return Q;
|
|
100
106
|
}
|
|
101
|
-
})(),
|
|
107
|
+
})(), qe = !j && (Q === "listening" || Q === "endpoint-grace"), Je = qe && w ? A("voiceSession.holding", "Take your time — tap “I’m done” when you finish") : qe && Le ? A("voiceSession.correcting", "Stopped that answer — listening to your correction") : Ke, $ = ne({
|
|
102
108
|
tr: A,
|
|
103
|
-
stage:
|
|
109
|
+
stage: Q,
|
|
104
110
|
muted: j,
|
|
105
|
-
label:
|
|
106
|
-
}),
|
|
107
|
-
return /* @__PURE__ */
|
|
111
|
+
label: Je
|
|
112
|
+
}), Ye = te(Je, $), Xe = Q === "thinking";
|
|
113
|
+
return /* @__PURE__ */ d("div", {
|
|
108
114
|
ref: P,
|
|
109
|
-
className: `flex flex-col items-center justify-between bg-bg-sunken ${
|
|
115
|
+
className: `flex flex-col items-center justify-between bg-bg-sunken ${L === "full" ? "fixed inset-x-0 bottom-0 top-14 z-[60] px-6 py-8" : L === "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"}`,
|
|
110
116
|
role: "dialog",
|
|
111
|
-
"aria-modal":
|
|
117
|
+
"aria-modal": z || void 0,
|
|
112
118
|
"aria-label": A("voiceSession.title", "Voice conversation"),
|
|
113
119
|
"data-testid": "assistant-voice-session",
|
|
114
120
|
[oe]: "",
|
|
115
121
|
children: [
|
|
116
|
-
/* @__PURE__ */
|
|
122
|
+
/* @__PURE__ */ d("button", {
|
|
117
123
|
ref: N,
|
|
118
124
|
type: "button",
|
|
119
|
-
onClick:
|
|
120
|
-
className: `absolute ${
|
|
125
|
+
onClick: C,
|
|
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`,
|
|
121
127
|
title: A("voiceSession.end", "End the voice conversation"),
|
|
122
128
|
"data-testid": "assistant-voice-end",
|
|
123
|
-
children: [/* @__PURE__ */
|
|
124
|
-
className:
|
|
129
|
+
children: [/* @__PURE__ */ u(l, {
|
|
130
|
+
className: z ? "size-6" : B ? "size-4" : "size-5",
|
|
125
131
|
"aria-hidden": "true"
|
|
126
|
-
}), /* @__PURE__ */
|
|
127
|
-
className: `truncate whitespace-nowrap font-medium ${
|
|
132
|
+
}), /* @__PURE__ */ u("span", {
|
|
133
|
+
className: `truncate whitespace-nowrap font-medium ${B ? "max-w-[3.5rem] text-xs" : "max-w-[7rem]"}`,
|
|
128
134
|
children: A("voiceSession.endShort", "End")
|
|
129
135
|
})]
|
|
130
136
|
}),
|
|
131
|
-
/* @__PURE__ */
|
|
132
|
-
className: `flex flex-col items-center ${
|
|
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" : ""}`}`,
|
|
133
139
|
children: [
|
|
134
|
-
|
|
140
|
+
Y ? /* @__PURE__ */ u("p", {
|
|
135
141
|
className: "max-w-xs text-center text-xs text-status-warning",
|
|
136
142
|
"data-testid": "assistant-voice-notice",
|
|
137
|
-
children:
|
|
143
|
+
children: p(h, A)
|
|
138
144
|
}) : null,
|
|
139
|
-
|
|
145
|
+
X ? /* @__PURE__ */ u("p", {
|
|
140
146
|
className: "max-w-xs text-center text-xs text-text-secondary",
|
|
141
147
|
"data-testid": "assistant-voice-avatar-hands-free",
|
|
142
|
-
children:
|
|
148
|
+
children: R.handsFreeOff
|
|
143
149
|
}) : null,
|
|
144
|
-
/* @__PURE__ */
|
|
150
|
+
/* @__PURE__ */ u("span", {
|
|
145
151
|
className: "sr-only start-0 top-0",
|
|
146
152
|
role: "status",
|
|
147
153
|
"aria-live": "polite",
|
|
148
154
|
"aria-atomic": "true",
|
|
149
155
|
"data-testid": "assistant-voice-notice-announced",
|
|
150
|
-
children:
|
|
156
|
+
children: Y && we(h) ? p(h, A) : X ? R.handsFreeOff : null
|
|
151
157
|
}),
|
|
152
|
-
|
|
158
|
+
x ? /* @__PURE__ */ u("p", {
|
|
153
159
|
className: "max-w-xs text-center text-xs text-status-warning",
|
|
154
160
|
role: "status",
|
|
155
161
|
"aria-live": "polite",
|
|
156
162
|
"data-testid": "assistant-voice-attachment-notice",
|
|
157
|
-
children:
|
|
163
|
+
children: x
|
|
158
164
|
}) : null
|
|
159
165
|
]
|
|
160
166
|
}),
|
|
161
167
|
(() => {
|
|
162
|
-
let e = /* @__PURE__ */
|
|
163
|
-
session:
|
|
164
|
-
compact:
|
|
165
|
-
connecting:
|
|
166
|
-
onPress: r(
|
|
167
|
-
label: r(
|
|
168
|
-
}), t =
|
|
168
|
+
let e = /* @__PURE__ */ u(fe, {
|
|
169
|
+
session: f,
|
|
170
|
+
compact: B,
|
|
171
|
+
connecting: Q === "connecting",
|
|
172
|
+
onPress: r(Q) ? Oe : v,
|
|
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", {
|
|
169
175
|
type: "button",
|
|
170
|
-
onClick:
|
|
171
|
-
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 ${
|
|
172
|
-
title:
|
|
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”"),
|
|
173
179
|
"data-testid": "assistant-voice-hold",
|
|
174
|
-
"data-holding":
|
|
175
|
-
children:
|
|
176
|
-
}) : null, n = t ? /* @__PURE__ */
|
|
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", {
|
|
177
183
|
className: "flex flex-col items-center gap-2",
|
|
178
184
|
children: [e, t]
|
|
179
|
-
}) : e, i = ee(
|
|
180
|
-
return
|
|
181
|
-
spokenChunks:
|
|
182
|
-
state:
|
|
185
|
+
}) : e, i = ee(Q) ? _ : We || null, a = M === null ? null : Te(M, A);
|
|
186
|
+
return B ? /* @__PURE__ */ u(pe, {
|
|
187
|
+
spokenChunks: S,
|
|
188
|
+
state: Q,
|
|
183
189
|
caption: i,
|
|
184
|
-
statusText:
|
|
185
|
-
statusSuffix:
|
|
186
|
-
rotateStatus:
|
|
190
|
+
statusText: Ye,
|
|
191
|
+
statusSuffix: $,
|
|
192
|
+
rotateStatus: Xe,
|
|
187
193
|
errorText: a,
|
|
188
194
|
control: n
|
|
189
|
-
}) : /* @__PURE__ */
|
|
190
|
-
state:
|
|
191
|
-
userCaption:
|
|
192
|
-
spokenChunks:
|
|
193
|
-
assistantCaption:
|
|
194
|
-
expanded:
|
|
195
|
-
statusText:
|
|
196
|
-
statusSuffix:
|
|
197
|
-
rotateStatus:
|
|
195
|
+
}) : /* @__PURE__ */ u(me, {
|
|
196
|
+
state: Q,
|
|
197
|
+
userCaption: We || null,
|
|
198
|
+
spokenChunks: S,
|
|
199
|
+
assistantCaption: _,
|
|
200
|
+
expanded: z,
|
|
201
|
+
statusText: Ye,
|
|
202
|
+
statusSuffix: $,
|
|
203
|
+
rotateStatus: Xe,
|
|
198
204
|
errorText: a,
|
|
199
205
|
control: n
|
|
200
206
|
});
|
|
201
207
|
})(),
|
|
202
|
-
|
|
203
|
-
className:
|
|
204
|
-
children: /* @__PURE__ */
|
|
208
|
+
D && !W ? /* @__PURE__ */ u("div", {
|
|
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)]"}`,
|
|
210
|
+
children: /* @__PURE__ */ u("button", {
|
|
205
211
|
type: "button",
|
|
206
212
|
onClick: () => {
|
|
207
|
-
F("panel"),
|
|
213
|
+
F("panel"), D.onOpen();
|
|
208
214
|
},
|
|
209
|
-
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 ${
|
|
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"}`,
|
|
210
216
|
"data-testid": "assistant-voice-proposal-chip",
|
|
211
|
-
children:
|
|
217
|
+
children: D.label
|
|
212
218
|
})
|
|
213
219
|
}) : null,
|
|
214
|
-
|
|
215
|
-
className: `absolute z-10 flex flex-col gap-2 overflow-y-auto rounded-lg border border-border-subtle bg-bg-elevated shadow-lg ${
|
|
220
|
+
O && W ? /* @__PURE__ */ d("div", {
|
|
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"}`,
|
|
216
222
|
"data-testid": "assistant-voice-proposal-tray",
|
|
217
|
-
children: [/* @__PURE__ */
|
|
223
|
+
children: [/* @__PURE__ */ u("div", {
|
|
218
224
|
className: "flex justify-end",
|
|
219
|
-
children: /* @__PURE__ */
|
|
225
|
+
children: /* @__PURE__ */ u("button", {
|
|
220
226
|
type: "button",
|
|
221
|
-
onClick:
|
|
227
|
+
onClick: O.onClose,
|
|
222
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",
|
|
223
|
-
"aria-label":
|
|
229
|
+
"aria-label": O.closeLabel,
|
|
224
230
|
"data-testid": "assistant-voice-proposal-tray-close",
|
|
225
|
-
children: /* @__PURE__ */
|
|
231
|
+
children: /* @__PURE__ */ u(l, {
|
|
226
232
|
className: "size-4",
|
|
227
233
|
"aria-hidden": "true"
|
|
228
234
|
})
|
|
229
235
|
})
|
|
230
|
-
}),
|
|
236
|
+
}), O.content]
|
|
231
237
|
}) : null,
|
|
232
|
-
|
|
233
|
-
view:
|
|
234
|
-
onContinue:
|
|
235
|
-
onNotNow:
|
|
238
|
+
g ? /* @__PURE__ */ u(ve, {
|
|
239
|
+
view: L,
|
|
240
|
+
onContinue: g.accept,
|
|
241
|
+
onNotNow: g.decline
|
|
236
242
|
}) : null,
|
|
237
|
-
|
|
238
|
-
turns:
|
|
239
|
-
onClose: () =>
|
|
240
|
-
onSave:
|
|
241
|
-
onContinueInChat:
|
|
242
|
-
renderAfterTurn:
|
|
243
|
+
H ? /* @__PURE__ */ u(he, {
|
|
244
|
+
turns: m,
|
|
245
|
+
onClose: () => U(!1),
|
|
246
|
+
onSave: Ee,
|
|
247
|
+
onContinueInChat: De,
|
|
248
|
+
renderAfterTurn: Re
|
|
243
249
|
}) : null,
|
|
244
|
-
|
|
245
|
-
className: `absolute ${
|
|
246
|
-
children: /* @__PURE__ */
|
|
250
|
+
ke ? /* @__PURE__ */ u("div", {
|
|
251
|
+
className: `absolute ${z ? "left-6 top-6" : B ? "left-11 top-2" : "left-3 top-3"}`,
|
|
252
|
+
children: /* @__PURE__ */ u(ue, {
|
|
247
253
|
onFiles: (e) => {
|
|
248
|
-
|
|
254
|
+
ke(e), F((e) => e === "mini" ? "panel" : e);
|
|
249
255
|
},
|
|
250
|
-
onPickerOpen:
|
|
256
|
+
onPickerOpen: Ue,
|
|
251
257
|
direction: "down",
|
|
252
|
-
canAddMore:
|
|
253
|
-
disabled:
|
|
258
|
+
canAddMore: Me,
|
|
259
|
+
disabled: Ne
|
|
254
260
|
})
|
|
255
261
|
}) : null,
|
|
256
|
-
(
|
|
257
|
-
className: `absolute ${
|
|
262
|
+
(y.length > 0 || b > 0) && !B ? /* @__PURE__ */ u("div", {
|
|
263
|
+
className: `absolute ${z ? "bottom-24 left-24 right-24" : "bottom-16 left-16 right-16"}`,
|
|
258
264
|
"data-testid": "assistant-voice-attachments",
|
|
259
|
-
children: /* @__PURE__ */
|
|
260
|
-
attachments:
|
|
261
|
-
onRemove:
|
|
262
|
-
onRetry:
|
|
265
|
+
children: /* @__PURE__ */ u(de, {
|
|
266
|
+
attachments: y,
|
|
267
|
+
onRemove: Ae ?? (() => void 0),
|
|
268
|
+
onRetry: je ?? (() => void 0),
|
|
263
269
|
preparingCount: b,
|
|
264
|
-
disabled:
|
|
270
|
+
disabled: Ne
|
|
265
271
|
})
|
|
266
272
|
}) : null,
|
|
267
|
-
/* @__PURE__ */
|
|
273
|
+
/* @__PURE__ */ u("button", {
|
|
268
274
|
type: "button",
|
|
269
|
-
onClick: () =>
|
|
270
|
-
className: `absolute ${
|
|
271
|
-
"aria-pressed":
|
|
275
|
+
onClick: () => U((e) => !e),
|
|
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`,
|
|
277
|
+
"aria-pressed": H,
|
|
272
278
|
"aria-label": A("voiceSession.transcriptTitle", "Voice transcript"),
|
|
273
279
|
"data-testid": "assistant-voice-transcript-toggle",
|
|
274
|
-
children: /* @__PURE__ */
|
|
280
|
+
children: /* @__PURE__ */ u(Ce, { className: z ? "size-6" : B ? "size-4" : "size-5" })
|
|
275
281
|
}),
|
|
276
|
-
/* @__PURE__ */
|
|
282
|
+
/* @__PURE__ */ u("button", {
|
|
277
283
|
type: "button",
|
|
278
|
-
onClick: () => F(
|
|
279
|
-
className: `absolute ${
|
|
280
|
-
"aria-label":
|
|
284
|
+
onClick: () => F(V),
|
|
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`,
|
|
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"),
|
|
281
287
|
"data-testid": "assistant-voice-expand",
|
|
282
|
-
children:
|
|
288
|
+
children: z ? /* @__PURE__ */ u(Se, { className: "size-5" }) : /* @__PURE__ */ u(xe, { className: B ? "size-4" : "size-5" })
|
|
283
289
|
})
|
|
284
290
|
]
|
|
285
291
|
});
|
|
286
292
|
};
|
|
287
|
-
function
|
|
293
|
+
function p(e, t) {
|
|
294
|
+
return typeof e == "string" ? m(e, t) : e.text;
|
|
295
|
+
}
|
|
296
|
+
function we(e) {
|
|
297
|
+
return e !== null && (typeof e != "string" || e === "reply-not-heard");
|
|
298
|
+
}
|
|
299
|
+
function m(e, t) {
|
|
288
300
|
switch (e) {
|
|
289
301
|
case "network": return t("voiceSession.noticeNetwork", "Speech recognition lost its connection. Check your network — it will retry.");
|
|
290
302
|
case "not-capturing": return t("voiceSession.noticeNotCapturing", "The microphone did not start. Check that this site is allowed to use it, then try again.");
|
|
@@ -296,7 +308,7 @@ function Se(e, t) {
|
|
|
296
308
|
default: return e;
|
|
297
309
|
}
|
|
298
310
|
}
|
|
299
|
-
function
|
|
311
|
+
function Te(e, t) {
|
|
300
312
|
switch (e) {
|
|
301
313
|
case "mic-denied": return t("voiceSession.micDenied", "Microphone access is blocked. Allow it in your browser settings to talk.");
|
|
302
314
|
case "no-speech-support": return t("voiceSession.noSpeechSupport", "This browser cannot listen. Try a different browser, or type instead.");
|
|
@@ -305,4 +317,4 @@ function Ce(e, t) {
|
|
|
305
317
|
}
|
|
306
318
|
}
|
|
307
319
|
//#endregion
|
|
308
|
-
export {
|
|
320
|
+
export { f as AssistantVoiceSession };
|
|
@@ -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 };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { AssistantConversationProcessing } from '../conversation';
|
|
2
|
+
/**
|
|
3
|
+
* What the voice session is handed as "the newest reply" (`useVoiceSession`'s
|
|
4
|
+
* `latestReply`).
|
|
5
|
+
*
|
|
6
|
+
* `refused` is set only when the turn ENDED IN AN ERROR: a refusal before it
|
|
7
|
+
* started ("No active subscription covers ...") or a failure. Its text is the
|
|
8
|
+
* reason, not an answer, and nothing was published to the speech feed for it, so
|
|
9
|
+
* the session must show it rather than enter `speaking` (BOFF-9207). A real
|
|
10
|
+
* answer is exactly `{ id, text }`, with no extra key, as it always was.
|
|
11
|
+
*
|
|
12
|
+
* `processing` is the entry for the message's OWN conversation; it is matched on
|
|
13
|
+
* the message id because one entry serves a whole conversation and outlives the
|
|
14
|
+
* turn, so an older turn's error must not mark a newer, answered one.
|
|
15
|
+
*/
|
|
16
|
+
export interface VoiceReply {
|
|
17
|
+
id: string;
|
|
18
|
+
text: string;
|
|
19
|
+
refused?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function toVoiceReply(message: {
|
|
22
|
+
id: string;
|
|
23
|
+
}, text: string, processing: AssistantConversationProcessing | null | undefined): VoiceReply;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/shared/assistant/voice/voiceReply.ts
|
|
2
|
+
function e(e, t, n) {
|
|
3
|
+
return n?.messageId === e.id && n.status === "error" ? {
|
|
4
|
+
id: e.id,
|
|
5
|
+
text: t,
|
|
6
|
+
refused: !0
|
|
7
|
+
} : {
|
|
8
|
+
id: e.id,
|
|
9
|
+
text: t
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { e as toVoiceReply };
|
|
@@ -118,6 +118,16 @@ export type VoiceSessionEvent =
|
|
|
118
118
|
| {
|
|
119
119
|
type: 'reply-audio-started';
|
|
120
120
|
replyId: string;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* ★ BOFF-9207: the turn ended WITHOUT an answer - the assistant refused it
|
|
124
|
+
* (a plan that does not cover it, a permission) or it failed before anything
|
|
125
|
+
* was said. There is nothing to speak, so the session must never enter
|
|
126
|
+
* `speaking` for it: only a `thinking` moves, straight back to `listening`.
|
|
127
|
+
* The reason is shown by the caller (`VoiceTurnRefusalNotice`), not spoken.
|
|
128
|
+
*/
|
|
129
|
+
| {
|
|
130
|
+
type: 'turn-refused';
|
|
121
131
|
} | {
|
|
122
132
|
type: 'speech-ended';
|
|
123
133
|
} | {
|
|
@@ -156,6 +156,13 @@ function v(t, r, i = null) {
|
|
|
156
156
|
pending: "",
|
|
157
157
|
interim: ""
|
|
158
158
|
} : t;
|
|
159
|
+
case "turn-refused": return t.phase === "thinking" ? {
|
|
160
|
+
...t,
|
|
161
|
+
...g,
|
|
162
|
+
phase: "listening",
|
|
163
|
+
pending: "",
|
|
164
|
+
interim: ""
|
|
165
|
+
} : t;
|
|
159
166
|
case "speech-ended": return t.phase === "speaking" ? {
|
|
160
167
|
...t,
|
|
161
168
|
...g,
|