@burdenoff/fe-libs 2026.1014.5 → 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 (39) 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 +50 -21
  16. package/dist/shared/assistant/avatar/useAvatarCopy.js +93 -49
  17. package/dist/shared/assistant/i18nManifest.d.ts +23 -2
  18. package/dist/shared/assistant/i18nManifest.js +23 -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 +134 -101
  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 +137 -5
  27. package/dist/shared/assistant/ui/assistantLiveAvatarSeam.js +114 -55
  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/panelOverlays.d.ts +24 -3
  33. package/dist/shared/assistant/ui/panelOverlays.js +11 -10
  34. package/dist/shared/assistant/ui/voice/AssistantVoiceSession.d.ts +6 -0
  35. package/dist/shared/assistant/ui/voice/AssistantVoiceSession.js +172 -166
  36. package/dist/shared/assistant/ui/voice/voiceStartGate.d.ts +57 -0
  37. package/dist/shared/assistant/ui/voice/voiceStartGate.js +9 -0
  38. package/dist/shared-assistant.js +259 -254
  39. package/package.json +1 -1
@@ -14,53 +14,72 @@ import { AssistantVoiceButton as ae, ListeningDot as oe } from "./AssistantVoice
14
14
  import { AssistantVoiceSessionButton as se } from "../voice/AssistantVoiceSessionButton.js";
15
15
  import { useAssistantAttachments as ce } from "../../useAssistantAttachments.js";
16
16
  import { useCallback as s, useEffect as c, useLayoutEffect as le, useMemo as ue, useRef as l, useState as u } from "react";
17
- import { ArrowUp as de, Loader2 as d } from "lucide-react";
18
- import { jsx as f, jsxs as p } from "react/jsx-runtime";
17
+ import { ArrowUp as de, Loader2 as fe } from "lucide-react";
18
+ import { jsx as d, jsxs as f } from "react/jsx-runtime";
19
19
  //#region src/shared/assistant/ui/composer/AssistantComposer.tsx
20
- var fe = 200, m = ({ writeAccessDenied: m = !1, onSend: h, disabled: g = !1, readOnly: pe = !1, placeholder: me, mode: he, availableModes: _, attachmentCharsPerFile: ge, onModeChange: _e, auto: ve, onStartVoiceSession: v, onDraftStart: y }) => {
21
- let b = r(), { locale: x } = e(), S = g || pe, C = l(null), [w, T] = u(""), [E, D] = u(!1), [O, k] = u(!1), A = l(!1), [j, M] = u(null), N = l(0), { attachments: P, isBusy: ye, preparingCount: be, sendBlocker: F, rejection: I, addFiles: L, remove: xe, retry: Se, clear: R, clearRejection: Ce, canAddMore: we, commit: z, cancelCommit: B } = ce({ perFileChars: ge }), V = w.trim() !== "" || P.length > 0, H = l(!1);
20
+ var pe = 200, p = ({ writeAccessDenied: p = !1, onSend: m, disabled: h = !1, readOnly: me = !1, placeholder: he, mode: ge, availableModes: g, attachmentCharsPerFile: _e, onModeChange: ve, auto: ye, onStartVoiceSession: be, onDraftStart: _, suspendDictation: v = !1, onVoiceStateChange: y }) => {
21
+ let b = r(), { locale: x } = e(), S = h || me, C = l(null), [w, T] = u(""), [E, D] = u(!1), [O, xe] = u(!1), k = l(!1), [A, j] = u(null), M = l(0), { attachments: N, isBusy: Se, preparingCount: Ce, sendBlocker: P, rejection: F, addFiles: I, remove: we, retry: Te, clear: L, clearRejection: Ee, canAddMore: De, commit: R, cancelCommit: z } = ce({ perFileChars: _e }), B = w.trim() !== "" || N.length > 0, V = l(!1);
22
22
  c(() => {
23
- V && !H.current ? (H.current = !0, y?.()) : V || (H.current = !1);
24
- }, [V, y]);
25
- let Te = s((e) => {
23
+ B && !V.current ? (V.current = !0, _?.()) : B || (V.current = !1);
24
+ }, [B, _]);
25
+ let H = s((e) => {
26
26
  T((t) => t ? `${t.replace(/\s+$/, "")} ${e}` : e);
27
27
  }, []), U = i((e) => e.assistantLanguage), W = n({
28
- onFinalize: Te,
28
+ onFinalize: H,
29
29
  language: ue(() => te(U, x), [U, x]).tag
30
- }), Ee = me ?? b("composer.placeholder", "Ask me anything…"), G = w.trim(), K = a(P), q = P.filter((e) => e.status === "error"), J = (G.length > 0 || K.length > 0) && !S && !O && F === null, Y = K.some((e) => !e.fileId && !e.inlineOnly), X = l(S);
31
- X.current = S;
30
+ }), G = W.isActive, K = W.interim, q = W.stop;
31
+ c(() => {
32
+ !v || !G || (K.trim() !== "" && H(K.trim()), q());
33
+ }, [
34
+ v,
35
+ G,
36
+ K,
37
+ q,
38
+ H
39
+ ]), c(() => {
40
+ y?.({
41
+ committing: O,
42
+ dictating: G
43
+ });
44
+ }, [
45
+ y,
46
+ O,
47
+ G
48
+ ]);
49
+ let Oe = he ?? b("composer.placeholder", "Ask me anything…"), ke = w.trim(), J = a(N), Y = N.filter((e) => e.status === "error"), X = (ke.length > 0 || J.length > 0) && !S && !O && P === null, Ae = J.some((e) => !e.fileId && !e.inlineOnly), je = l(S);
50
+ je.current = S;
32
51
  let Z = l(!0);
33
52
  c(() => (Z.current = !0, () => {
34
53
  Z.current = !1;
35
54
  }), []), le(() => {
36
55
  let e = C.current;
37
- e && (e.style.height = "auto", e.style.height = `${Math.min(e.scrollHeight, fe)}px`);
56
+ e && (e.style.height = "auto", e.style.height = `${Math.min(e.scrollHeight, pe)}px`);
38
57
  }, [w]);
39
58
  let Q = s(async () => {
40
- if (!J || A.current) return;
59
+ if (!X || k.current) return;
41
60
  W.isActive && W.stop();
42
61
  let e = w.trim();
43
- if (M(null), !Y) {
44
- h(e, K), T(""), R();
62
+ if (j(null), !Ae) {
63
+ m(e, J), T(""), L();
45
64
  return;
46
65
  }
47
66
  let t = i.getState().activeConversationId;
48
- A.current = !0, k(!0);
67
+ k.current = !0, xe(!0);
49
68
  try {
50
- let n = await z();
69
+ let n = await R();
51
70
  if (!Z.current) {
52
71
  n.ok && await n.rollback();
53
72
  return;
54
73
  }
55
74
  if (!n.ok) {
56
- n.cancelled || M({
75
+ n.cancelled || j({
57
76
  kind: "failed",
58
77
  failures: n.failures
59
78
  });
60
79
  return;
61
80
  }
62
- if (X.current) {
63
- await n.rollback(), Z.current && M({
81
+ if (je.current) {
82
+ await n.rollback(), Z.current && j({
64
83
  kind: "busy",
65
84
  failures: []
66
85
  });
@@ -68,52 +87,52 @@ var fe = 200, m = ({ writeAccessDenied: m = !1, onSend: h, disabled: g = !1, rea
68
87
  }
69
88
  let r = i.getState();
70
89
  if (!(r.activeConversationId === t && (t === null || r.conversations.some((e) => e.id === t)))) {
71
- await n.rollback(), Z.current && M({
90
+ await n.rollback(), Z.current && j({
72
91
  kind: "moved",
73
92
  failures: []
74
93
  });
75
94
  return;
76
95
  }
77
- h(e, a(n.attachments)), n.handedOver(), T(""), R();
96
+ m(e, a(n.attachments)), n.handedOver(), T(""), L();
78
97
  } finally {
79
- A.current = !1, Z.current && k(!1);
98
+ k.current = !1, Z.current && xe(!1);
80
99
  }
81
100
  }, [
82
- J,
83
- Y,
84
- z,
85
- h,
86
- w,
87
- K,
101
+ X,
102
+ Ae,
88
103
  R,
104
+ m,
105
+ w,
106
+ J,
107
+ L,
89
108
  W
90
- ]), $ = s(() => {
91
- B(), C.current?.focus();
92
- }, [B]);
109
+ ]), Me = s(() => {
110
+ z(), C.current?.focus();
111
+ }, [z]);
93
112
  c(() => {
94
- M(null);
95
- }, [P.length]);
96
- let De = (e) => {
113
+ j(null);
114
+ }, [N.length]);
115
+ let Ne = (e) => {
97
116
  if (e.kind === "busy") return b("attachments.saveBusy", "The assistant started another reply, so nothing was sent. Your message and files are still here.");
98
117
  if (e.kind === "moved") return b("attachments.saveMoved", "You switched conversations while the files were being saved, so nothing was sent. Your message and files are still here.");
99
118
  let { failures: t } = e;
100
119
  if (t.some((e) => e.code === o.quotaExceeded || e.code === o.quotaNotProvisioned)) return `${b("attachments.quotaFull", "File storage is full or not set up for this workspace, so the attachments couldn't be saved. Nothing was sent.")} ${b("attachments.retrySend", "Press Send to try again, or remove the file.")}`;
101
- let n = t[0], r = P.find((e) => e.id === n?.id)?.filename ?? "";
120
+ let n = t[0], r = N.find((e) => e.id === n?.id)?.filename ?? "";
102
121
  return `${t.length <= 1 ? b("attachments.saveFailedOne", "{{name}} couldn't be saved, so nothing was sent.", { name: r }) : b("attachments.saveFailedMany", "{{count}} attachments couldn't be saved, so nothing was sent.", { count: t.length })} ${b("attachments.retrySend", "Press Send to try again, or remove the file.")}`;
103
- }, Oe = s((e) => {
122
+ }, Pe = s((e) => {
104
123
  e.key === "Enter" && !e.shiftKey && (e.preventDefault(), Q());
105
- }, [Q]), ke = s((e) => {
124
+ }, [Q]), Fe = s((e) => {
106
125
  let t = Array.from(e.clipboardData?.files ?? []);
107
- t.length !== 0 && (e.preventDefault(), L(t));
108
- }, [L]), Ae = s((e) => {
109
- Array.from(e.dataTransfer?.types ?? []).includes("Files") && (N.current += 1, D(!0));
110
- }, []), je = s(() => {
111
- N.current = Math.max(0, N.current - 1), N.current === 0 && D(!1);
112
- }, []), Me = s((e) => {
126
+ t.length !== 0 && (e.preventDefault(), I(t));
127
+ }, [I]), Ie = s((e) => {
128
+ Array.from(e.dataTransfer?.types ?? []).includes("Files") && (M.current += 1, D(!0));
129
+ }, []), Le = s(() => {
130
+ M.current = Math.max(0, M.current - 1), M.current === 0 && D(!1);
131
+ }, []), Re = s((e) => {
113
132
  Array.from(e.dataTransfer?.types ?? []).includes("Files") && e.preventDefault();
114
- }, []), Ne = s((e) => {
115
- e.preventDefault(), N.current = 0, D(!1), L(e.dataTransfer?.files ?? null);
116
- }, [L]), Pe = (e) => {
133
+ }, []), ze = s((e) => {
134
+ e.preventDefault(), M.current = 0, D(!1), I(e.dataTransfer?.files ?? null);
135
+ }, [I]), Be = (e) => {
117
136
  switch (e) {
118
137
  case "too-many": return b("attachments.tooMany", "You can attach up to {{max}} files per message.", { max: 5 });
119
138
  case "too-large": return b("attachments.tooLarge", "That file is larger than 10 MB.");
@@ -122,7 +141,7 @@ var fe = 200, m = ({ writeAccessDenied: m = !1, onSend: h, disabled: g = !1, rea
122
141
  case "unsupported-image": return b("attachments.unsupportedImage", "This device can't read that image format, so it can't be resized to send. Save it as JPEG and try again.");
123
142
  case "too-large-to-save": return b("attachments.tooLargeToSave", "That file is over 5 MB, so it can't be saved to Files, and no text could be read from it.");
124
143
  }
125
- }, Fe = (() => {
144
+ }, $ = (() => {
126
145
  switch (W.error) {
127
146
  case "not-allowed": return b("voice.denied", "Microphone access is blocked. Allow it in your browser settings to dictate.");
128
147
  case "audio-capture": return b("voice.noMic", "No microphone was found.");
@@ -132,136 +151,136 @@ var fe = 200, m = ({ writeAccessDenied: m = !1, onSend: h, disabled: g = !1, rea
132
151
  default: return null;
133
152
  }
134
153
  })();
135
- return /* @__PURE__ */ p("div", {
154
+ return /* @__PURE__ */ f("div", {
136
155
  className: "flex-shrink-0 px-3 pb-3 pt-1.5",
137
156
  children: [
138
- /* @__PURE__ */ p("div", {
139
- onDragEnter: Ae,
140
- onDragLeave: je,
141
- onDragOver: Me,
142
- onDrop: Ne,
157
+ /* @__PURE__ */ f("div", {
158
+ onDragEnter: Ie,
159
+ onDragLeave: Le,
160
+ onDragOver: Re,
161
+ onDrop: ze,
143
162
  className: `rounded-2xl border p-2 shadow-sm transition-colors focus-within:border-action-primary-bg focus-within:ring-2 focus-within:ring-action-primary-bg/15 ${E ? "border-action-primary-bg border-dashed bg-action-primary-bg/5" : "border-border-default bg-bg-sunken"}`,
144
163
  "data-testid": "assistant-composer",
145
164
  children: [
146
- /* @__PURE__ */ f(re, {
147
- attachments: P,
148
- onRemove: xe,
149
- onRetry: Se,
165
+ /* @__PURE__ */ d(re, {
166
+ attachments: N,
167
+ onRemove: we,
168
+ onRetry: Te,
150
169
  disabled: S || O,
151
- preparingCount: be
170
+ preparingCount: Ce
152
171
  }),
153
- /* @__PURE__ */ p("div", {
172
+ /* @__PURE__ */ f("div", {
154
173
  className: "flex items-end gap-2",
155
174
  children: [
156
- /* @__PURE__ */ f(ne, {
157
- onFiles: L,
158
- canAddMore: we,
175
+ /* @__PURE__ */ d(ne, {
176
+ onFiles: I,
177
+ canAddMore: De,
159
178
  disabled: S || O,
160
179
  showLabel: !0
161
180
  }),
162
- /* @__PURE__ */ f("textarea", {
181
+ /* @__PURE__ */ d("textarea", {
163
182
  ref: C,
164
183
  value: w,
165
184
  onChange: (e) => T(e.target.value),
166
- onKeyDown: Oe,
167
- onPaste: ke,
185
+ onKeyDown: Pe,
186
+ onPaste: Fe,
168
187
  disabled: S,
169
188
  readOnly: O,
170
189
  "aria-busy": O || void 0,
171
- placeholder: E ? b("composer.dropHere", "Drop files to attach") : Ee,
190
+ placeholder: E ? b("composer.dropHere", "Drop files to attach") : Oe,
172
191
  rows: 1,
173
192
  className: "min-h-[36px] flex-1 resize-none self-center bg-transparent px-1 py-2 text-[15px] leading-relaxed text-text-primary placeholder:text-text-muted focus:outline-none disabled:opacity-50",
174
193
  "aria-label": b("chatInput.ariaLabel", "Message input"),
175
194
  "data-testid": "assistant-input"
176
195
  }),
177
- W.isSupported && /* @__PURE__ */ f(ae, {
196
+ W.isSupported && /* @__PURE__ */ d(ae, {
178
197
  isListening: W.isListening,
179
198
  isStarting: W.isStarting,
180
199
  onToggle: W.toggle,
181
200
  disabled: S || O
182
201
  }),
183
- v && G.length === 0 && P.length === 0 && !W.isActive ? /* @__PURE__ */ f(se, {
184
- onStart: v,
202
+ be && ke.length === 0 && N.length === 0 && !W.isActive ? /* @__PURE__ */ d(se, {
203
+ onStart: be,
185
204
  disabled: S || O
186
- }) : /* @__PURE__ */ f("button", {
205
+ }) : /* @__PURE__ */ d("button", {
187
206
  type: "button",
188
207
  onClick: () => void Q(),
189
- disabled: !J,
190
- className: `flex size-9 flex-shrink-0 items-center justify-center rounded-full shadow-sm transition-all focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong ${J ? "bg-action-primary-bg text-action-primary-text hover:scale-105 hover:bg-action-primary-bg-hover" : "bg-bg-elevated text-text-muted"}`,
208
+ disabled: !X,
209
+ className: `flex size-9 flex-shrink-0 items-center justify-center rounded-full shadow-sm transition-all focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong ${X ? "bg-action-primary-bg text-action-primary-text hover:scale-105 hover:bg-action-primary-bg-hover" : "bg-bg-elevated text-text-muted"}`,
191
210
  "aria-label": b("chatInput.send", "Send message"),
192
211
  "data-testid": "assistant-send",
193
- children: g || ye || O ? /* @__PURE__ */ f(d, { className: "size-4 motion-safe:animate-spin" }) : /* @__PURE__ */ f(de, { className: "size-4" })
212
+ children: h || Se || O ? /* @__PURE__ */ d(fe, { className: "size-4 motion-safe:animate-spin" }) : /* @__PURE__ */ d(de, { className: "size-4" })
194
213
  })
195
214
  ]
196
215
  }),
197
- _.length > 1 && /* @__PURE__ */ f("div", {
216
+ g.length > 1 && /* @__PURE__ */ d("div", {
198
217
  className: "mt-2 px-0.5",
199
- children: /* @__PURE__ */ f(ie, {
200
- mode: he,
201
- availableModes: _,
202
- onModeChange: _e,
218
+ children: /* @__PURE__ */ d(ie, {
219
+ mode: ge,
220
+ availableModes: g,
221
+ onModeChange: ve,
203
222
  disabled: S || O,
204
- auto: ve
223
+ auto: ye
205
224
  })
206
225
  })
207
226
  ]
208
227
  }),
209
- m ? /* @__PURE__ */ f("p", {
228
+ p ? /* @__PURE__ */ d("p", {
210
229
  className: "mt-1.5 px-1 text-center text-[11px] text-status-warning",
211
230
  "data-testid": "assistant-write-access-warning",
212
231
  children: b("composer.writeAccessDenied", "Changes may not save — your role in this workspace does not currently allow edits. You can still plan and review an action.")
213
232
  }) : null,
214
- /* @__PURE__ */ f("div", {
233
+ /* @__PURE__ */ d("div", {
215
234
  className: "mt-1.5 px-1 text-center text-[11px]",
216
235
  "aria-live": "polite",
217
- children: O ? /* @__PURE__ */ f(t, {
218
- onDismiss: $,
219
- children: /* @__PURE__ */ p("p", {
236
+ children: O ? /* @__PURE__ */ d(t, {
237
+ onDismiss: Me,
238
+ children: /* @__PURE__ */ f("p", {
220
239
  className: "flex items-center justify-center gap-2 text-text-muted",
221
240
  "data-testid": "assistant-commit-status",
222
241
  children: [
223
- /* @__PURE__ */ f(d, {
242
+ /* @__PURE__ */ d(fe, {
224
243
  className: "size-3 motion-safe:animate-spin",
225
244
  "aria-hidden": "true"
226
245
  }),
227
- /* @__PURE__ */ f("span", { children: b("attachments.saving", "Saving your attachments…") }),
228
- /* @__PURE__ */ f("button", {
246
+ /* @__PURE__ */ d("span", { children: b("attachments.saving", "Saving your attachments…") }),
247
+ /* @__PURE__ */ d("button", {
229
248
  type: "button",
230
- onClick: $,
249
+ onClick: Me,
231
250
  className: "rounded text-text-primary underline underline-offset-2 hover:text-action-primary-bg focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong",
232
251
  "data-testid": "assistant-commit-cancel",
233
252
  children: b("attachments.cancelSaving", "Cancel")
234
253
  })
235
254
  ]
236
255
  })
237
- }) : j ? /* @__PURE__ */ f("p", {
256
+ }) : A ? /* @__PURE__ */ d("p", {
238
257
  className: "text-status-error-text",
239
258
  role: "alert",
240
259
  "data-testid": "assistant-commit-failed",
241
- children: De(j)
242
- }) : W.isStarting && !W.isListening ? /* @__PURE__ */ f("p", {
260
+ children: Ne(A)
261
+ }) : W.isStarting && !W.isListening ? /* @__PURE__ */ d("p", {
243
262
  className: "text-text-muted",
244
263
  "data-testid": "assistant-voice-starting",
245
264
  children: b("voice.starting", "Starting the microphone…")
246
- }) : W.isListening ? /* @__PURE__ */ p("p", {
265
+ }) : W.isListening ? /* @__PURE__ */ f("p", {
247
266
  className: "text-status-error-text",
248
267
  "data-testid": "assistant-voice-status",
249
- children: [/* @__PURE__ */ f(oe, {}), W.interim || b("voice.listening", "Listening… speak now, tap the square to stop")]
250
- }) : Fe ? /* @__PURE__ */ f("p", {
268
+ children: [/* @__PURE__ */ d(oe, {}), W.interim || b("voice.listening", "Listening… speak now, tap the square to stop")]
269
+ }) : $ ? /* @__PURE__ */ d("p", {
251
270
  className: "text-status-error-text",
252
271
  "data-testid": "assistant-voice-error",
253
- children: Fe
254
- }) : I ? /* @__PURE__ */ f("button", {
272
+ children: $
273
+ }) : F ? /* @__PURE__ */ d("button", {
255
274
  type: "button",
256
- onClick: Ce,
275
+ onClick: Ee,
257
276
  className: "text-status-error-text underline-offset-2 hover:underline",
258
277
  "data-testid": "assistant-attachment-rejection",
259
- children: Pe(I)
260
- }) : F === "failed" ? /* @__PURE__ */ f("p", {
278
+ children: Be(F)
279
+ }) : P === "failed" ? /* @__PURE__ */ d("p", {
261
280
  className: "text-status-error-text",
262
281
  "data-testid": "assistant-attachment-blocked",
263
- children: q.length === 1 ? b("attachments.blockedOne", "{{name}} didn't upload. Retry or remove it before sending.", { name: q[0].filename }) : b("attachments.blockedMany", "{{count}} attachments didn't upload. Retry or remove them before sending.", { count: q.length })
264
- }) : /* @__PURE__ */ f("p", {
282
+ children: Y.length === 1 ? b("attachments.blockedOne", "{{name}} didn't upload. Retry or remove it before sending.", { name: Y[0].filename }) : b("attachments.blockedMany", "{{count}} attachments didn't upload. Retry or remove them before sending.", { count: Y.length })
283
+ }) : /* @__PURE__ */ d("p", {
265
284
  className: "text-text-muted",
266
285
  children: b("composer.hint", "Enter to send · Shift+Enter for newline · drag or paste files to attach")
267
286
  })
@@ -270,4 +289,4 @@ var fe = 200, m = ({ writeAccessDenied: m = !1, onSend: h, disabled: g = !1, rea
270
289
  });
271
290
  };
272
291
  //#endregion
273
- export { m as AssistantComposer };
292
+ export { p as AssistantComposer };
@@ -33,6 +33,20 @@ export declare const NO_PANEL_OVERLAY: PanelOverlayState;
33
33
  * other one swaps to it. There is deliberately no way to express "both".
34
34
  */
35
35
  export declare function nextPanelOverlays(current: PanelOverlayState, toggled: "history" | "preview"): PanelOverlayState;
36
+ /**
37
+ * The overlay slot, plus the Avatar tab (BOFF-9341) where the panel offers it.
38
+ *
39
+ * ★★ The tab is NOT part of the slot. It is a view of the chat area drawn
40
+ * under History and the preview (`AssistantAvatarTab`, `z-[5]` below their
41
+ * `z-10`, the voice strip's `z-20` and the transcript drawer's `z-30`), so
42
+ * History can open OVER it and closing History shows it again. Escape backs
43
+ * out one layer at a time: the transcript drawer, the voice session, the tab,
44
+ * then the panel.
45
+ */
46
+ export interface PanelLayerState extends PanelOverlayState {
47
+ /** The Avatar tab is the view on screen; absent where the panel offers none. */
48
+ readonly avatarOpen?: boolean;
49
+ }
36
50
  /**
37
51
  * What Escape should dismiss.
38
52
  *
@@ -44,7 +58,7 @@ export declare function nextPanelOverlays(current: PanelOverlayState, toggled: "
44
58
  * Escape is "back out of the thing I just opened"; here it destroyed a
45
59
  * conversation instead.
46
60
  */
47
- export declare function escapeDismisses(current: PanelOverlayState): "overlay" | "panel";
61
+ export declare function escapeDismisses(current: PanelLayerState): "overlay" | "tab" | "panel";
48
62
  /**
49
63
  * Attribute marking the assistant panel's root element.
50
64
  *
@@ -94,9 +108,16 @@ export declare const ASSISTANT_VOICE_SESSION_ATTR = "data-assistant-voice-sessio
94
108
  * comes first. The panel backs out of it, as `escapeDismisses` says, and marks
95
109
  * the key handled so the session does not also act on it.
96
110
  */
97
- export declare function voiceSessionOwnsEscape(panel: Pick<Element, "hasAttribute" | "querySelector">): boolean;
111
+ export declare function voiceSessionOwnsEscape(panel: Pick<Element, "hasAttribute" | "querySelector"> & Partial<Pick<Element, "getAttribute">>): boolean;
112
+ /**
113
+ * `data-assistant-overlay` while the Avatar tab is the view on screen and no
114
+ * overlay covers it (BOFF-9341): the panel then owns an Escape pressed with
115
+ * focus on `<body>` (a control in the tab that unmounted itself on click), so
116
+ * it selects Chat again instead of the host closing the whole assistant.
117
+ */
118
+ export declare const AVATAR_TAB_OVERLAY = "avatar";
98
119
  /** Which overlay is showing, as the attribute value — `null` when none is. */
99
- export declare function activePanelOverlay(current: PanelOverlayState): "history" | "preview" | null;
120
+ export declare function activePanelOverlay(current: PanelLayerState): "history" | "preview" | typeof AVATAR_TAB_OVERLAY | null;
100
121
  /**
101
122
  * Is `active` the panel itself, or inside it?
102
123
  *
@@ -10,24 +10,25 @@ function t(t, n) {
10
10
  };
11
11
  }
12
12
  function n(e) {
13
- return e.historyOpen || e.previewOpen ? "overlay" : "panel";
13
+ return e.historyOpen || e.previewOpen ? "overlay" : e.avatarOpen === !0 ? "tab" : "panel";
14
14
  }
15
15
  var r = "data-assistant-panel", i = "data-assistant-overlay", a = "data-assistant-voice-session";
16
16
  function o(e) {
17
- return e.hasAttribute("data-assistant-overlay") ? !1 : e.querySelector(`[${a}]`) !== null;
17
+ return e.hasAttribute("data-assistant-overlay") && e.getAttribute?.("data-assistant-overlay") !== "avatar" ? !1 : e.querySelector(`[${a}]`) !== null;
18
18
  }
19
- function s(e) {
20
- return e.historyOpen ? "history" : e.previewOpen ? "preview" : null;
19
+ var s = "avatar";
20
+ function c(e) {
21
+ return e.historyOpen ? "history" : e.previewOpen ? "preview" : e.avatarOpen === !0 ? s : null;
21
22
  }
22
- function c(e, t) {
23
+ function l(e, t) {
23
24
  return !e || !t ? !1 : e === t || e.contains(t);
24
25
  }
25
- function l(e = document) {
26
+ function u(e = document) {
26
27
  let t = e.activeElement;
27
- return Array.from(e.querySelectorAll(`[${r}]`)).some((n) => u(n, t, e.body));
28
+ return Array.from(e.querySelectorAll(`[${r}]`)).some((n) => d(n, t, e.body));
28
29
  }
29
- function u(e, t, n) {
30
- return e ? c(e, t) ? !0 : (t === null || t === n) && e.hasAttribute("data-assistant-overlay") : !1;
30
+ function d(e, t, n) {
31
+ return e ? l(e, t) ? !0 : (t === null || t === n) && e.hasAttribute("data-assistant-overlay") : !1;
31
32
  }
32
33
  //#endregion
33
- export { i as ASSISTANT_OVERLAY_ATTR, r as ASSISTANT_PANEL_ATTR, a as ASSISTANT_VOICE_SESSION_ATTR, e as NO_PANEL_OVERLAY, s as activePanelOverlay, l as assistantPanelOwnsEscape, n as escapeDismisses, c as focusIsInsidePanel, t as nextPanelOverlays, u as panelOwnsEscape, o as voiceSessionOwnsEscape };
34
+ export { i as ASSISTANT_OVERLAY_ATTR, r as ASSISTANT_PANEL_ATTR, a as ASSISTANT_VOICE_SESSION_ATTR, s as AVATAR_TAB_OVERLAY, e as NO_PANEL_OVERLAY, c as activePanelOverlay, u as assistantPanelOwnsEscape, n as escapeDismisses, l as focusIsInsidePanel, t as nextPanelOverlays, d as panelOwnsEscape, o as voiceSessionOwnsEscape };
@@ -98,6 +98,12 @@ interface Props {
98
98
  * — the page the model offered in that voice turn.
99
99
  */
100
100
  renderAfterTurn?: (turnId: string) => ReactNode;
101
+ /**
102
+ * ★ BOFF-9341: told the view shown, while the panel has the Avatar tab: an
103
+ * enlarged session covers the tab, which then is no stage (D4). Absent, the
104
+ * session reports nothing.
105
+ */
106
+ onViewChange?: (view: VoiceView) => void;
101
107
  }
102
108
  /**
103
109
  * Full-screen voice conversation (BOFF-7120).