@burdenoff/fe-libs 2026.1016.2 → 2026.1017.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.
- package/dist/shared/assistant/avatar/live/AvatarFace.d.ts +13 -0
- package/dist/shared/assistant/avatar/live/AvatarFace.js +46 -0
- package/dist/shared/assistant/avatar/live/LiveAvatarTabView.d.ts +2 -0
- package/dist/shared/assistant/avatar/live/LiveAvatarTabView.js +172 -145
- package/dist/shared/assistant/avatar/live/avatarFaces.d.ts +31 -0
- package/dist/shared/assistant/avatar/live/avatarFaces.js +33 -0
- package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.d.ts +12 -0
- package/dist/shared/assistant/avatar/live/createLiveAvatarSeam.js +142 -75
- package/dist/shared/assistant/avatar/live/liveAvatarController.d.ts +5 -0
- package/dist/shared/assistant/avatar/live/liveAvatarController.js +3 -1
- package/dist/shared/assistant/avatar/live/liveAvatarStatus.d.ts +14 -0
- package/dist/shared/assistant/avatar/live/liveAvatarStatus.js +11 -2
- package/dist/shared/assistant/avatar/useAvatarCopy.d.ts +42 -0
- package/dist/shared/assistant/avatar/useAvatarCopy.js +113 -75
- package/dist/shared/assistant/i18nManifest.d.ts +29 -0
- package/dist/shared/assistant/i18nManifest.js +29 -0
- package/dist/shared/assistant/index.d.ts +8 -0
- package/dist/shared/assistant/speech/hostedTtsSpeechEngine.d.ts +6 -0
- package/dist/shared/assistant/speech/hostedTtsSpeechEngine.js +118 -101
- package/dist/shared/assistant/speech/notifyCancellationSpeechEngine.js +3 -0
- package/dist/shared/assistant/speech/replySpeechRequests.d.ts +84 -0
- package/dist/shared/assistant/speech/replySpeechRequests.js +70 -0
- package/dist/shared/assistant/speech/routingSpeechEngine.js +13 -9
- package/dist/shared/assistant/speech/types.d.ts +39 -1
- package/dist/shared/assistant/ui/AIAssistantPanel.js +128 -119
- package/dist/shared/assistant/ui/AssistantChatArea.js +1572 -1430
- package/dist/shared/assistant/ui/AssistantMessageItem.js +110 -100
- package/dist/shared/assistant/ui/AssistantSpeechProvider.d.ts +10 -0
- package/dist/shared/assistant/ui/AssistantSpeechProvider.js +286 -225
- package/dist/shared/assistant/ui/assistantLiveAvatarSeam.d.ts +40 -0
- package/dist/shared/assistant/ui/avatar/AssistantAvatarTab.d.ts +6 -0
- package/dist/shared/assistant/ui/avatar/AssistantAvatarTab.js +99 -52
- package/dist/shared/assistant/ui/avatar/AvatarTabAskBox.d.ts +8 -0
- package/dist/shared/assistant/ui/avatar/AvatarTabAskBox.js +87 -0
- package/dist/shared/assistant/ui/avatar/AvatarTabLastExchange.d.ts +13 -0
- package/dist/shared/assistant/ui/avatar/AvatarTabLastExchange.js +97 -0
- package/dist/shared/assistant/ui/avatar/avatarAskGate.d.ts +104 -0
- package/dist/shared/assistant/ui/avatar/avatarAskGate.js +39 -0
- package/dist/shared/assistant/ui/avatar/avatarTabDraft.d.ts +17 -0
- package/dist/shared/assistant/ui/avatar/avatarTabDraft.js +22 -0
- package/dist/shared/assistant/ui/avatar/avatarTabExchange.d.ts +47 -0
- package/dist/shared/assistant/ui/avatar/avatarTabExchange.js +50 -0
- package/dist/shared/assistant/ui/panelOverlays.d.ts +8 -1
- package/dist/shared/assistant/ui/panelOverlays.js +1 -1
- package/dist/shared/assistant/ui/useVoiceSession.js +222 -227
- package/dist/shared/assistant/voice/handsfree/playbackReference.d.ts +15 -4
- package/dist/shared/assistant/voice/handsfree/playbackReference.js +9 -6
- package/dist/shared-assistant.js +270 -262
- package/package.json +1 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
export interface AvatarFaceProps {
|
|
3
|
+
/** The face's display name: the initials, and the frame picture's `alt`. */
|
|
4
|
+
readonly name: string;
|
|
5
|
+
/** The picture's URL, already sanitised (`sanitizeAvatarImages`); absent: the initials (pill) or nothing (frame). */
|
|
6
|
+
readonly src?: string | null;
|
|
7
|
+
readonly size: 'pill' | 'frame';
|
|
8
|
+
/** The pill is the selected one: drawn on the filled pill's colours. */
|
|
9
|
+
readonly selected?: boolean;
|
|
10
|
+
/** The picture failed to load, once per `src`. */
|
|
11
|
+
readonly onError?: (src: string) => void;
|
|
12
|
+
}
|
|
13
|
+
export declare const AvatarFace: FC<AvatarFaceProps>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { faceInitials as e } from "./avatarFaces.js";
|
|
2
|
+
import { useState as t } from "react";
|
|
3
|
+
import { jsx as n } from "react/jsx-runtime";
|
|
4
|
+
//#region src/shared/assistant/avatar/live/AvatarFace.tsx
|
|
5
|
+
var r = "relative inline-flex size-8 shrink-0 items-center justify-center overflow-hidden rounded-full", i = ({ name: i, src: a, size: o, selected: s = !1, onError: c }) => {
|
|
6
|
+
let [l, u] = t(null), d = typeof a == "string" && a.length > 0 && l !== a ? a : null, f = (e) => {
|
|
7
|
+
u(e), c?.(e);
|
|
8
|
+
};
|
|
9
|
+
return o === "frame" ? d === null ? null : /* @__PURE__ */ n("img", {
|
|
10
|
+
src: d,
|
|
11
|
+
alt: i,
|
|
12
|
+
width: 320,
|
|
13
|
+
height: 320,
|
|
14
|
+
loading: "lazy",
|
|
15
|
+
decoding: "async",
|
|
16
|
+
draggable: !1,
|
|
17
|
+
referrerPolicy: "no-referrer",
|
|
18
|
+
onError: () => f(d),
|
|
19
|
+
className: "absolute inset-0 size-full object-cover",
|
|
20
|
+
"data-testid": "assistant-avatar-face-frame"
|
|
21
|
+
}) : d === null ? /* @__PURE__ */ n("span", {
|
|
22
|
+
"aria-hidden": "true",
|
|
23
|
+
className: `${r} border text-sm font-medium ${s ? "border-transparent bg-action-primary-text text-action-primary-bg" : "border-border-subtle bg-bg-sunken text-text-secondary"}`,
|
|
24
|
+
"data-testid": "assistant-avatar-face",
|
|
25
|
+
"data-face": "initials",
|
|
26
|
+
children: e(i)
|
|
27
|
+
}) : /* @__PURE__ */ n("span", {
|
|
28
|
+
className: `${r} ring-1 ${s ? "ring-action-primary-text/40" : "ring-border-subtle"}`,
|
|
29
|
+
"data-testid": "assistant-avatar-face",
|
|
30
|
+
"data-face": "picture",
|
|
31
|
+
children: /* @__PURE__ */ n("img", {
|
|
32
|
+
src: d,
|
|
33
|
+
alt: "",
|
|
34
|
+
width: 32,
|
|
35
|
+
height: 32,
|
|
36
|
+
loading: "lazy",
|
|
37
|
+
decoding: "async",
|
|
38
|
+
draggable: !1,
|
|
39
|
+
referrerPolicy: "no-referrer",
|
|
40
|
+
onError: () => f(d),
|
|
41
|
+
className: "size-full object-cover"
|
|
42
|
+
})
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
//#endregion
|
|
46
|
+
export { i as AvatarFace };
|
|
@@ -16,6 +16,8 @@ export interface LiveAvatarTabViewProps extends LiveAvatarTabProps {
|
|
|
16
16
|
readonly controller: LiveAvatarController;
|
|
17
17
|
/** Display names for the avatar ids the server offers; without one the chooser numbers them. */
|
|
18
18
|
readonly avatarNames?: Readonly<Record<string, string>>;
|
|
19
|
+
/** BOFF-9362: a picture per avatar id, already sanitised (`sanitizeAvatarImages`); a face without one shows its initials. */
|
|
20
|
+
readonly avatarImages?: ReadonlyMap<string, string>;
|
|
19
21
|
/** The host's speaker per avatar ("Default for this avatar"). */
|
|
20
22
|
readonly avatarVoices: LiveAvatarVoices;
|
|
21
23
|
readonly defaultAvatarVoiceMode?: AvatarVoiceDefault;
|
|
@@ -4,270 +4,297 @@ import { useAssistantSpeech as ee } from "../../ui/AssistantSpeechProvider.js";
|
|
|
4
4
|
import { resolveAvatarVoiceMode as te } from "../avatarPreferences.js";
|
|
5
5
|
import "./liveAvatarController.js";
|
|
6
6
|
import { liveAvatarStatusText as ne } from "./liveAvatarStatus.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
7
|
+
import { AvatarFace as r } from "./AvatarFace.js";
|
|
8
|
+
import { hostedAvatarVoice as i } from "./avatarVoice.js";
|
|
9
|
+
import { useCallback as a, useEffect as o, useId as s, useLayoutEffect as re, useRef as c, useState as l, useSyncExternalStore as u } from "react";
|
|
10
|
+
import { Volume2 as ie } from "lucide-react";
|
|
11
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
11
12
|
//#region src/shared/assistant/avatar/live/LiveAvatarTabView.tsx
|
|
12
|
-
var
|
|
13
|
-
function
|
|
14
|
-
let n =
|
|
13
|
+
var p = "mine", m = "paired", h = "speaker:", g = /-Chirp3-HD-([A-Za-z]+)$/;
|
|
14
|
+
function _(e) {
|
|
15
|
+
let n = g.exec(e)?.[1];
|
|
15
16
|
return n !== void 0 && t(n) ? n : e;
|
|
16
17
|
}
|
|
17
|
-
function
|
|
18
|
+
function v(e, t) {
|
|
18
19
|
let n = t.find((e) => e.source === "hosted")?.lang;
|
|
19
|
-
return n === void 0 ? null :
|
|
20
|
+
return n === void 0 ? null : i(e, n, t);
|
|
20
21
|
}
|
|
21
|
-
var
|
|
22
|
-
let D = n(), O = ee(), k =
|
|
23
|
-
|
|
24
|
-
if (
|
|
25
|
-
}, [
|
|
26
|
-
let e =
|
|
27
|
-
e.phase === "off" && e.avatars.length === 0 &&
|
|
28
|
-
}, [
|
|
29
|
-
let { phase:
|
|
30
|
-
|
|
31
|
-
let
|
|
32
|
-
let t =
|
|
33
|
-
return
|
|
34
|
-
},
|
|
22
|
+
var y = "text-xs font-medium text-text-secondary", b = "text-xs text-text-muted", x = "focus:outline-none focus-visible:ring-2 focus-visible:ring-border-strong", S = `inline-flex min-h-11 w-full items-center justify-center gap-2 rounded-xl border border-border-default bg-bg-elevated px-4 text-sm font-medium text-text-primary transition-colors hover:bg-bg-raised aria-disabled:cursor-default aria-disabled:opacity-60 aria-disabled:hover:bg-bg-elevated ${x}`, ae = `inline-flex min-h-11 w-full items-center justify-center rounded-xl bg-action-primary-bg px-4 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg-hover aria-disabled:cursor-default aria-disabled:opacity-60 aria-disabled:hover:bg-action-primary-bg ${x}`, oe = "relative inline-flex min-h-11 min-w-0 max-w-full cursor-pointer items-center gap-2 rounded-full border ps-1.5 pe-4 text-sm font-medium transition-colors focus-within:ring-2 focus-within:ring-border-strong", se = "absolute inset-0 m-0 size-full cursor-pointer appearance-none rounded-full opacity-0", C = ({ controller: i, shown: g, voice: C, handsFreeOff: ce, leaveHintId: le, startVoiceRef: ue, askLine: w, avatarNames: de, avatarImages: fe, avatarVoices: pe, defaultAvatarVoiceMode: me, voiceStore: T, now: E = Date.now }) => {
|
|
23
|
+
let D = n(), O = ee(), k = s(), he = s(), A = s(), j = s(), M = s(), N = c(null), P = u(i.subscribe, i.getSnapshot, i.getSnapshot), F = u(T.subscribe, T.getState, T.getState);
|
|
24
|
+
o(() => {
|
|
25
|
+
if (g) return i.setStageMounted(!0), () => i.setStageMounted(!1);
|
|
26
|
+
}, [i, g]), o(() => {
|
|
27
|
+
let e = i.getSnapshot();
|
|
28
|
+
e.phase === "off" && e.avatars.length === 0 && i.requestOn({ start: !1 });
|
|
29
|
+
}, [i]);
|
|
30
|
+
let { phase: I } = P, L = I === "starting" || I === "connecting" || I === "ready" || I === "ending", [R, z] = l(!1);
|
|
31
|
+
R && (I === "off" || I === "choosing" || I === "ready" || I === "ending") && z(!1);
|
|
32
|
+
let B = E(), ge = P.retryAt !== null && P.retryAt > B, V = ne(P, D, B), H = P.selectedAvatarId, U = (e) => {
|
|
33
|
+
let t = P.avatars.indexOf(e);
|
|
34
|
+
return de?.[e] ?? (t === -1 ? D.chooserNone : `${D.chooserLabel} ${t + 1}`);
|
|
35
|
+
}, W = I === "ready" ? { kind: "turn-off" } : I === "ending" ? { kind: "ending" } : R || I === "starting" || I === "connecting" ? { kind: "starting" } : P.offered ? {
|
|
35
36
|
kind: "turn-on",
|
|
36
|
-
ready: !
|
|
37
|
-
} : null,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}, [
|
|
41
|
-
let
|
|
37
|
+
ready: !ge && H !== null && (I === "choosing" || I === "off" && P.avatars.includes(H))
|
|
38
|
+
} : null, G = c(null), K = W?.kind === "turn-off";
|
|
39
|
+
re(() => {
|
|
40
|
+
G.current = K ? E() : null;
|
|
41
|
+
}, [K, E]);
|
|
42
|
+
let _e = a((e) => {
|
|
42
43
|
if (e !== null) return () => {
|
|
43
|
-
e.ownerDocument.activeElement === e &&
|
|
44
|
+
e.ownerDocument.activeElement === e && N.current?.focus();
|
|
44
45
|
};
|
|
45
|
-
}, []),
|
|
46
|
-
if (
|
|
46
|
+
}, []), ve = () => {
|
|
47
|
+
if (W !== null) switch (W.kind) {
|
|
47
48
|
case "turn-on":
|
|
48
|
-
if (!
|
|
49
|
-
|
|
49
|
+
if (!W.ready || H === null) return;
|
|
50
|
+
z(!0), I === "choosing" ? i.confirm() : i.requestOn({
|
|
50
51
|
start: !0,
|
|
51
|
-
avatarId:
|
|
52
|
+
avatarId: H
|
|
52
53
|
});
|
|
53
54
|
return;
|
|
54
55
|
case "turn-off": {
|
|
55
|
-
let e =
|
|
56
|
+
let e = G.current;
|
|
56
57
|
if (e !== null && E() - e < 1e3) return;
|
|
57
|
-
|
|
58
|
+
i.switchOff();
|
|
58
59
|
return;
|
|
59
60
|
}
|
|
60
61
|
case "starting":
|
|
61
62
|
case "ending": return;
|
|
62
63
|
}
|
|
63
|
-
},
|
|
64
|
-
if (e ===
|
|
64
|
+
}, q = te(F, me), J = H === null ? null : pe.get(H) ?? null, Y = q === "speaker" && F.avatarSpeaker !== null ? `${h}${F.avatarSpeaker}` : q === "paired" && J !== null ? m : p, ye = Y === m ? J : Y.startsWith(h) ? F.avatarSpeaker : null, X = Y === p ? O.activeVoiceName : ye === null ? null : v(ye, O.voiceChoices)?.name ?? null, be = O.speakingMessageId !== null || O.spokenChunks !== null, xe = X !== null && !C.live && !be, Se = O.activeVoiceName ? D.voiceModeMineNamed(_(O.activeVoiceName)) : D.voiceModeMine, Ce = (e) => {
|
|
65
|
+
if (e === p || e === m) {
|
|
65
66
|
T.setVoiceMode(e);
|
|
66
67
|
return;
|
|
67
68
|
}
|
|
68
69
|
let n = e.slice(8);
|
|
69
|
-
e.startsWith(
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
e.startsWith(h) && t(n) && T.setAvatarSpeaker(n);
|
|
71
|
+
}, we = I === "ending" ? D.stateEnding : I === "connecting" || I === "ready" ? D.stageWaiting : I === "starting" ? D.stateStarting : D.stateOff, [Te, Ee] = l(() => /* @__PURE__ */ new Set()), De = a((e) => {
|
|
72
|
+
Ee((t) => t.has(e) ? t : new Set(t).add(e));
|
|
73
|
+
}, []), Z = (e) => {
|
|
74
|
+
let t = fe?.get(e);
|
|
75
|
+
return t === void 0 || Te.has(t) ? null : t;
|
|
76
|
+
}, Q = !L && H !== null ? Z(H) : null, $ = L ? null : w === void 0 ? C.impossible ? D.askNeedsVoice : null : w === "by-typing" ? D.askByTyping : w === "needs-voice" ? D.askNeedsVoice : w === "nothing" ? D.askNothing : null, Oe = I === "off" && P.avatars.length === 0 && P.problem !== null && P.offered;
|
|
77
|
+
return /* @__PURE__ */ f("div", {
|
|
78
|
+
ref: N,
|
|
73
79
|
tabIndex: -1,
|
|
74
80
|
className: "flex min-h-full shrink-0 flex-col gap-3 focus:outline-none",
|
|
75
81
|
"data-testid": "assistant-avatar-tab-view",
|
|
76
82
|
children: [
|
|
77
|
-
/* @__PURE__ */
|
|
83
|
+
/* @__PURE__ */ d("div", {
|
|
78
84
|
className: "flex max-h-80 min-h-[7.5rem] grow basis-30 justify-center",
|
|
79
|
-
children: /* @__PURE__ */
|
|
85
|
+
children: /* @__PURE__ */ f("div", {
|
|
80
86
|
className: "relative aspect-square h-full max-w-full overflow-hidden rounded-2xl border border-border-subtle bg-bg-sunken",
|
|
81
87
|
"data-testid": "assistant-avatar-tab-frame",
|
|
82
|
-
"data-phase":
|
|
88
|
+
"data-phase": I,
|
|
83
89
|
children: [
|
|
84
|
-
|
|
85
|
-
ref:
|
|
86
|
-
className: `size-full object-cover ${
|
|
90
|
+
L ? /* @__PURE__ */ d("video", {
|
|
91
|
+
ref: i.attachVideo,
|
|
92
|
+
className: `size-full object-cover ${P.videoReady ? "" : "invisible"}`,
|
|
87
93
|
"aria-label": D.stageLabel,
|
|
88
94
|
autoPlay: !0,
|
|
89
95
|
muted: !0,
|
|
90
96
|
playsInline: !0,
|
|
91
97
|
"data-testid": "assistant-avatar-tab-video"
|
|
92
98
|
}) : null,
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
99
|
+
Q !== null && H !== null ? /* @__PURE__ */ d(r, {
|
|
100
|
+
size: "frame",
|
|
101
|
+
name: U(H),
|
|
102
|
+
src: Q,
|
|
103
|
+
onError: De
|
|
104
|
+
}) : null,
|
|
105
|
+
L && P.videoReady ? null : /* @__PURE__ */ d("p", {
|
|
106
|
+
className: Q === null ? "absolute inset-0 flex items-center justify-center p-3 text-center text-sm text-text-secondary" : "absolute inset-x-0 bottom-0 bg-bg-elevated/90 px-3 py-1.5 text-center text-xs text-text-secondary",
|
|
107
|
+
"data-testid": "assistant-avatar-tab-frame-text",
|
|
108
|
+
children: we
|
|
96
109
|
}),
|
|
97
|
-
|
|
110
|
+
P.audioBlocked ? /* @__PURE__ */ f("button", {
|
|
98
111
|
type: "button",
|
|
99
|
-
onClick: () =>
|
|
100
|
-
className: `absolute inset-x-2 bottom-2 inline-flex min-h-11 items-center justify-center gap-1.5 rounded-xl bg-bg-elevated px-3 text-xs font-medium text-text-primary ${
|
|
112
|
+
onClick: () => i.resumeAudio(),
|
|
113
|
+
className: `absolute inset-x-2 bottom-2 inline-flex min-h-11 items-center justify-center gap-1.5 rounded-xl bg-bg-elevated px-3 text-xs font-medium text-text-primary ${x}`,
|
|
101
114
|
"data-testid": "assistant-avatar-tab-sound",
|
|
102
|
-
children: [/* @__PURE__ */
|
|
115
|
+
children: [/* @__PURE__ */ d(ie, {
|
|
103
116
|
className: "size-4 shrink-0",
|
|
104
117
|
"aria-hidden": "true"
|
|
105
|
-
}), /* @__PURE__ */
|
|
118
|
+
}), /* @__PURE__ */ d("span", { children: D.problem("playback-blocked") })]
|
|
106
119
|
}) : null
|
|
107
120
|
]
|
|
108
121
|
})
|
|
109
122
|
}),
|
|
110
|
-
/* @__PURE__ */
|
|
111
|
-
className:
|
|
123
|
+
/* @__PURE__ */ d("p", {
|
|
124
|
+
className: V === null ? "sr-only" : "text-center text-sm text-text-secondary",
|
|
112
125
|
role: "status",
|
|
113
126
|
"data-testid": "assistant-avatar-tab-status",
|
|
114
|
-
children:
|
|
127
|
+
children: V ?? ""
|
|
115
128
|
}),
|
|
116
|
-
|
|
129
|
+
P.avatars.length > 1 && !L ? /* @__PURE__ */ f("fieldset", {
|
|
117
130
|
className: "flex min-w-0 flex-col gap-1.5",
|
|
118
131
|
"data-testid": "assistant-avatar-tab-chooser",
|
|
119
|
-
children: [/* @__PURE__ */
|
|
120
|
-
className:
|
|
132
|
+
children: [/* @__PURE__ */ d("legend", {
|
|
133
|
+
className: y,
|
|
121
134
|
children: D.chooserTitle
|
|
122
|
-
}), /* @__PURE__ */
|
|
135
|
+
}), /* @__PURE__ */ d("div", {
|
|
123
136
|
className: "flex flex-wrap gap-2",
|
|
124
|
-
children:
|
|
125
|
-
let t = e ===
|
|
126
|
-
return /* @__PURE__ */
|
|
137
|
+
children: P.avatars.map((e) => {
|
|
138
|
+
let t = e === H, n = U(e);
|
|
139
|
+
return /* @__PURE__ */ f("label", {
|
|
127
140
|
className: `${oe} ${t ? "border-transparent bg-action-primary-bg text-action-primary-text" : "border-border-default bg-bg-elevated text-text-secondary hover:bg-bg-raised hover:text-text-primary"}`,
|
|
128
141
|
title: t ? D.chooserSelected(n) : D.chooserOption(n),
|
|
129
|
-
children: [
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
children: [
|
|
143
|
+
/* @__PURE__ */ d("input", {
|
|
144
|
+
type: "radio",
|
|
145
|
+
name: he,
|
|
146
|
+
value: e,
|
|
147
|
+
checked: t,
|
|
148
|
+
onChange: () => i.selectAvatar(e),
|
|
149
|
+
className: se,
|
|
150
|
+
"data-avatar-id": e
|
|
151
|
+
}),
|
|
152
|
+
/* @__PURE__ */ d(r, {
|
|
153
|
+
size: "pill",
|
|
154
|
+
name: n,
|
|
155
|
+
src: Z(e),
|
|
156
|
+
selected: t,
|
|
157
|
+
onError: De
|
|
158
|
+
}),
|
|
159
|
+
/* @__PURE__ */ d("span", {
|
|
160
|
+
className: "truncate",
|
|
161
|
+
children: n
|
|
162
|
+
})
|
|
163
|
+
]
|
|
141
164
|
}, e);
|
|
142
165
|
})
|
|
143
166
|
})]
|
|
144
|
-
}) :
|
|
167
|
+
}) : P.avatars.length > 0 && H !== null ? /* @__PURE__ */ f("div", {
|
|
145
168
|
className: "flex flex-col gap-0.5",
|
|
146
169
|
"data-testid": "assistant-avatar-tab-chosen",
|
|
147
|
-
children: [/* @__PURE__ */
|
|
170
|
+
children: [/* @__PURE__ */ f("p", {
|
|
148
171
|
className: "flex min-w-0 items-baseline gap-2 text-sm",
|
|
149
|
-
children: [/* @__PURE__ */
|
|
150
|
-
className:
|
|
172
|
+
children: [/* @__PURE__ */ d("span", {
|
|
173
|
+
className: y,
|
|
151
174
|
children: D.chooserLabel
|
|
152
|
-
}), /* @__PURE__ */
|
|
175
|
+
}), /* @__PURE__ */ d("span", {
|
|
153
176
|
className: "truncate text-text-primary",
|
|
154
|
-
children:
|
|
177
|
+
children: U(H)
|
|
155
178
|
})]
|
|
156
|
-
}),
|
|
157
|
-
className:
|
|
179
|
+
}), L && P.avatars.length > 1 ? /* @__PURE__ */ d("p", {
|
|
180
|
+
className: b,
|
|
158
181
|
children: D.chooserLocked
|
|
159
182
|
}) : null]
|
|
160
|
-
}) :
|
|
161
|
-
className:
|
|
183
|
+
}) : I === "choosing" ? /* @__PURE__ */ d("p", {
|
|
184
|
+
className: b,
|
|
162
185
|
"data-testid": "assistant-avatar-tab-empty",
|
|
163
186
|
children: D.chooserEmpty
|
|
164
187
|
}) : null,
|
|
165
|
-
/* @__PURE__ */
|
|
188
|
+
/* @__PURE__ */ f("div", {
|
|
166
189
|
className: "flex flex-col gap-1",
|
|
167
190
|
children: [
|
|
168
|
-
/* @__PURE__ */
|
|
169
|
-
htmlFor:
|
|
170
|
-
className:
|
|
191
|
+
/* @__PURE__ */ d("label", {
|
|
192
|
+
htmlFor: A,
|
|
193
|
+
className: y,
|
|
171
194
|
children: D.voiceModeLabel
|
|
172
195
|
}),
|
|
173
|
-
/* @__PURE__ */
|
|
196
|
+
/* @__PURE__ */ f("div", {
|
|
174
197
|
className: "flex items-center gap-2",
|
|
175
|
-
children: [/* @__PURE__ */
|
|
176
|
-
id:
|
|
177
|
-
value:
|
|
178
|
-
onChange: (e) =>
|
|
179
|
-
"aria-describedby":
|
|
180
|
-
className: `min-h-11 min-w-0 flex-1 rounded-xl border border-border-default bg-bg-elevated px-3 text-sm text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary ${
|
|
198
|
+
children: [/* @__PURE__ */ f("select", {
|
|
199
|
+
id: A,
|
|
200
|
+
value: Y,
|
|
201
|
+
onChange: (e) => Ce(e.target.value),
|
|
202
|
+
"aria-describedby": j,
|
|
203
|
+
className: `min-h-11 min-w-0 flex-1 rounded-xl border border-border-default bg-bg-elevated px-3 text-sm text-text-primary scheme-normal dark:scheme-dark [&_option]:bg-bg-surface [&_option]:text-text-primary ${x}`,
|
|
181
204
|
"data-testid": "assistant-avatar-tab-voice",
|
|
182
205
|
children: [
|
|
183
|
-
/* @__PURE__ */
|
|
184
|
-
value: f,
|
|
185
|
-
children: ge
|
|
186
|
-
}),
|
|
187
|
-
Y === null ? null : /* @__PURE__ */ u("option", {
|
|
206
|
+
/* @__PURE__ */ d("option", {
|
|
188
207
|
value: p,
|
|
189
|
-
children:
|
|
208
|
+
children: Se
|
|
190
209
|
}),
|
|
191
|
-
|
|
192
|
-
value:
|
|
210
|
+
J === null ? null : /* @__PURE__ */ d("option", {
|
|
211
|
+
value: m,
|
|
212
|
+
children: D.voiceModeDefaultNamed(J)
|
|
213
|
+
}),
|
|
214
|
+
e.map((e) => /* @__PURE__ */ d("option", {
|
|
215
|
+
value: `${h}${e}`,
|
|
193
216
|
children: e
|
|
194
217
|
}, e))
|
|
195
218
|
]
|
|
196
|
-
}), /* @__PURE__ */
|
|
219
|
+
}), /* @__PURE__ */ d("button", {
|
|
197
220
|
type: "button",
|
|
198
221
|
onClick: () => {
|
|
199
|
-
|
|
222
|
+
xe && X !== null && O.previewVoice(X);
|
|
200
223
|
},
|
|
201
|
-
"aria-disabled":
|
|
224
|
+
"aria-disabled": !xe,
|
|
202
225
|
"aria-label": D.hearVoice,
|
|
203
226
|
title: D.hearVoice,
|
|
204
|
-
className: `inline-flex size-11 shrink-0 items-center justify-center rounded-xl border border-border-default bg-bg-elevated text-text-secondary transition-colors hover:bg-bg-raised hover:text-text-primary aria-disabled:cursor-default aria-disabled:opacity-60 ${
|
|
227
|
+
className: `inline-flex size-11 shrink-0 items-center justify-center rounded-xl border border-border-default bg-bg-elevated text-text-secondary transition-colors hover:bg-bg-raised hover:text-text-primary aria-disabled:cursor-default aria-disabled:opacity-60 ${x}`,
|
|
205
228
|
"data-testid": "assistant-avatar-tab-hear",
|
|
206
|
-
children: /* @__PURE__ */
|
|
229
|
+
children: /* @__PURE__ */ d(ie, {
|
|
207
230
|
className: "size-5",
|
|
208
231
|
"aria-hidden": "true"
|
|
209
232
|
})
|
|
210
233
|
})]
|
|
211
234
|
}),
|
|
212
|
-
/* @__PURE__ */
|
|
213
|
-
id:
|
|
214
|
-
className:
|
|
235
|
+
/* @__PURE__ */ d("p", {
|
|
236
|
+
id: j,
|
|
237
|
+
className: b,
|
|
215
238
|
children: D.voiceModeHint
|
|
216
239
|
})
|
|
217
240
|
]
|
|
218
241
|
}),
|
|
219
|
-
|
|
242
|
+
L ? null : /* @__PURE__ */ d("p", {
|
|
220
243
|
id: k,
|
|
221
244
|
className: "text-xs text-text-secondary",
|
|
222
245
|
"data-testid": "assistant-avatar-tab-disclosure",
|
|
223
246
|
children: D.consent
|
|
224
247
|
}),
|
|
225
|
-
|
|
226
|
-
ref:
|
|
248
|
+
W === null ? null : /* @__PURE__ */ d("button", {
|
|
249
|
+
ref: _e,
|
|
227
250
|
type: "button",
|
|
228
|
-
onClick:
|
|
229
|
-
"aria-disabled":
|
|
230
|
-
"aria-describedby":
|
|
231
|
-
className:
|
|
251
|
+
onClick: ve,
|
|
252
|
+
"aria-disabled": W.kind === "turn-on" ? !W.ready : W.kind !== "turn-off",
|
|
253
|
+
"aria-describedby": W.kind === "turn-on" ? k : void 0,
|
|
254
|
+
className: W.kind === "turn-off" ? S : ae,
|
|
232
255
|
"data-testid": "assistant-avatar-tab-switch",
|
|
233
|
-
"data-slot":
|
|
234
|
-
children:
|
|
256
|
+
"data-slot": W.kind,
|
|
257
|
+
children: W.kind === "turn-off" ? D.stop : W.kind === "starting" ? D.turnOnStarting : W.kind === "ending" ? D.stateEnding : D.consentConfirm
|
|
235
258
|
}),
|
|
236
|
-
|
|
259
|
+
Oe ? /* @__PURE__ */ d("button", {
|
|
237
260
|
type: "button",
|
|
238
|
-
onClick: () =>
|
|
239
|
-
className:
|
|
261
|
+
onClick: () => i.requestOn({ start: !1 }),
|
|
262
|
+
className: S,
|
|
240
263
|
"data-testid": "assistant-avatar-tab-retry",
|
|
241
264
|
children: D.chooserRetry
|
|
242
265
|
}) : null,
|
|
243
|
-
|
|
266
|
+
C.live ? null : /* @__PURE__ */ f("div", {
|
|
244
267
|
className: "flex flex-col gap-1",
|
|
245
|
-
children: [/* @__PURE__ */
|
|
246
|
-
ref:
|
|
268
|
+
children: [/* @__PURE__ */ d("button", {
|
|
269
|
+
ref: ue,
|
|
247
270
|
type: "button",
|
|
248
271
|
onClick: () => {
|
|
249
|
-
|
|
272
|
+
C.blocked === null && C.start();
|
|
250
273
|
},
|
|
251
|
-
"aria-disabled":
|
|
252
|
-
"aria-describedby":
|
|
253
|
-
className:
|
|
274
|
+
"aria-disabled": C.blocked !== null,
|
|
275
|
+
"aria-describedby": C.blocked === null ? void 0 : M,
|
|
276
|
+
className: S,
|
|
254
277
|
"data-testid": "assistant-avatar-tab-start-voice",
|
|
255
278
|
children: D.startVoice
|
|
256
|
-
}),
|
|
257
|
-
|
|
258
|
-
|
|
279
|
+
}), C.blocked === null ? $ === null ? null : /* @__PURE__ */ d("p", {
|
|
280
|
+
className: b,
|
|
281
|
+
"data-testid": "assistant-avatar-tab-ask-note",
|
|
282
|
+
children: $
|
|
283
|
+
}) : /* @__PURE__ */ d("p", {
|
|
284
|
+
id: M,
|
|
285
|
+
className: b,
|
|
259
286
|
"data-testid": "assistant-avatar-tab-voice-blocked",
|
|
260
|
-
children:
|
|
287
|
+
children: $ === null ? C.blocked : `${C.blocked} ${$}`
|
|
261
288
|
})]
|
|
262
289
|
}),
|
|
263
|
-
|
|
264
|
-
className:
|
|
290
|
+
ce && !P.voiceLive ? /* @__PURE__ */ d("p", {
|
|
291
|
+
className: b,
|
|
265
292
|
"data-testid": "assistant-avatar-tab-hands-free",
|
|
266
293
|
children: D.handsFreeOff
|
|
267
294
|
}) : null,
|
|
268
|
-
|
|
269
|
-
id:
|
|
270
|
-
className:
|
|
295
|
+
L && !P.heldByVoice ? /* @__PURE__ */ d("p", {
|
|
296
|
+
id: le,
|
|
297
|
+
className: b,
|
|
271
298
|
"data-testid": "assistant-avatar-tab-leave-hint",
|
|
272
299
|
children: D.tabLeaveHint
|
|
273
300
|
}) : null
|
|
@@ -275,4 +302,4 @@ var v = "text-xs font-medium text-text-secondary", y = "text-xs text-text-muted"
|
|
|
275
302
|
});
|
|
276
303
|
};
|
|
277
304
|
//#endregion
|
|
278
|
-
export {
|
|
305
|
+
export { C as LiveAvatarTabView, v as avatarPreviewVoice, _ as avatarVoiceDisplayName };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The face pictures of the Avatar tab's chooser and frame (BOFF-9362): the host's pictures, sanitised once, and the
|
|
3
|
+
* initials shown for a face without one. Pure, no React.
|
|
4
|
+
*
|
|
5
|
+
* ★★ NO PICTURE SHIPS IN FE-LIBS. The package is public on npm, and the rights to stills of the provider's faces are
|
|
6
|
+
* the operator's question. Each host imports its own pictures as hashed assets (`avatarImages`, next to its
|
|
7
|
+
* `avatarNames`); a host that gives none shows initials.
|
|
8
|
+
*
|
|
9
|
+
* ★★ A picture is a URL the page fetches, so only two shapes pass: a root-relative path (a hashed asset the app
|
|
10
|
+
* imported, `/assets/...`) or an `https://` URL, and only for an image: its path (the query and hash aside) ends in
|
|
11
|
+
* `.webp`, `.avif`, `.png`, `.jpg`, `.jpeg`, `.gif` or `.svg`. Everything else is dropped silently: `javascript:`,
|
|
12
|
+
* `http:`, a protocol-relative `//host`, `data:`, `blob:`, a page or an endpoint (`/api/logout`), an empty or
|
|
13
|
+
* non-string value, and an id that is not an avatar id. It is only ever an `<img>` source (an SVG there runs no
|
|
14
|
+
* script); the extension keeps a host's typo from making every chooser fetch a page.
|
|
15
|
+
*
|
|
16
|
+
* ★ A host trap: Vite inlines an imported asset under its `assetsInlineLimit` (4 KB by default) as a `data:` URL,
|
|
17
|
+
* which this drops, so a very small face file shows initials. The recommended 320 px WebP is well above it.
|
|
18
|
+
*/
|
|
19
|
+
/** Is `value` a picture URL the chooser may load? */
|
|
20
|
+
export declare function isAvatarImageUrl(value: unknown): value is string;
|
|
21
|
+
/**
|
|
22
|
+
* The host's `avatarImages`, as the view reads it: copied once (a host that changes its object later changes nothing
|
|
23
|
+
* here), with every entry that is not an avatar id mapped to a picture URL dropped. Never throws.
|
|
24
|
+
*/
|
|
25
|
+
export declare function sanitizeAvatarImages(raw: unknown): ReadonlyMap<string, string>;
|
|
26
|
+
/**
|
|
27
|
+
* The initials of a face's name, for the placeholder: the first grapheme of the first word and, when there is more
|
|
28
|
+
* than one, of the LAST word ("Mary Ann Lee" gives "ML"), upper-cased in the reader's locale. Whole graphemes, so a
|
|
29
|
+
* Tamil, Hindi or Arabic name and a combined character are never cut. An empty name gives "?".
|
|
30
|
+
*/
|
|
31
|
+
export declare function faceInitials(name: string): string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { resolveStoredAvatarId as e } from "../avatarPreferences.js";
|
|
2
|
+
//#region src/shared/assistant/avatar/live/avatarFaces.ts
|
|
3
|
+
var t = /^\/(?![/\\])[^\\\s]*$/, n = /^https:\/\/[^/\\\s]+(?:[/?#][^\\\s]*)?$/i, r = /^https:\/\/[^/?#]*/i, i = /\.(?:webp|avif|png|jpe?g|gif|svg)$/i;
|
|
4
|
+
function a(e) {
|
|
5
|
+
let t = e.replace(r, ""), n = t.search(/[?#]/);
|
|
6
|
+
return n === -1 ? t : t.slice(0, n);
|
|
7
|
+
}
|
|
8
|
+
function o(e) {
|
|
9
|
+
return typeof e != "string" || e.length === 0 || e.length > 2048 || !t.test(e) && !n.test(e) ? !1 : i.test(a(e));
|
|
10
|
+
}
|
|
11
|
+
function s(t) {
|
|
12
|
+
let n = /* @__PURE__ */ new Map();
|
|
13
|
+
if (typeof t != "object" || !t || Array.isArray(t)) return n;
|
|
14
|
+
for (let [r, i] of Object.entries(t)) e(r) === r && o(i) && n.set(r, i);
|
|
15
|
+
return n;
|
|
16
|
+
}
|
|
17
|
+
var c;
|
|
18
|
+
function l() {
|
|
19
|
+
return c === void 0 && (c = typeof Intl < "u" && typeof Intl.Segmenter == "function" ? new Intl.Segmenter(void 0, { granularity: "grapheme" }) : null), c;
|
|
20
|
+
}
|
|
21
|
+
function u(e) {
|
|
22
|
+
let t = l();
|
|
23
|
+
if (t !== null) for (let { segment: n } of t.segment(e)) return n;
|
|
24
|
+
return Array.from(e)[0] ?? "";
|
|
25
|
+
}
|
|
26
|
+
function d(e) {
|
|
27
|
+
let t = e.trim().split(/\s+/u).filter((e) => e.length > 0), n = t[0];
|
|
28
|
+
if (n === void 0) return "?";
|
|
29
|
+
let r = t.length > 1 ? t[t.length - 1] : void 0;
|
|
30
|
+
return (u(n) + (r === void 0 ? "" : u(r))).toLocaleUpperCase();
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { d as faceInitials, o as isAvatarImageUrl, s as sanitizeAvatarImages };
|
|
@@ -10,6 +10,18 @@ import { LiveAvatarController, LiveAvatarControllerDeps, LiveAvatarLogEvent, Liv
|
|
|
10
10
|
export interface LiveAvatarSeamOptions {
|
|
11
11
|
/** Display names for the avatar ids the server offers (it sends ids only); without one the chooser numbers them. */
|
|
12
12
|
readonly avatarNames?: Readonly<Record<string, string>>;
|
|
13
|
+
/**
|
|
14
|
+
* BOFF-9362: a picture per avatar id, shown in the Avatar tab's chooser and, while the avatar is off, in its frame. Host config,
|
|
15
|
+
* like `avatarNames` (the server sends ids only): a root-relative path (a hashed asset the app imported) or an `https://` URL, whose
|
|
16
|
+
* path ends in an image extension (`.webp`, `.avif`, `.png`, `.jpg`, `.jpeg`, `.gif`, `.svg`). An id with no entry, or a value of
|
|
17
|
+
* any other shape (`data:`, `blob:`, `http:`, `//host`, `javascript:`, a page), shows the face's initials. Copied once, here: a host
|
|
18
|
+
* that changes its object later changes nothing. ★ fe-libs ships no picture (it is a public package).
|
|
19
|
+
*
|
|
20
|
+
* ★ Vite inlines an imported asset under `build.assetsInlineLimit` (4 KB by default) as a `data:` URL, which is dropped here: a
|
|
21
|
+
* very small face file shows initials with no warning. Keep each face file above the limit (the recommended 320 px WebP is), or
|
|
22
|
+
* exclude the faces folder from inlining.
|
|
23
|
+
*/
|
|
24
|
+
readonly avatarImages?: Readonly<Record<string, string>>;
|
|
13
25
|
/**
|
|
14
26
|
* The speaker each avatar id speaks with as "Default for this avatar" (BOFF-9341): host config, never inferred. An id without one has
|
|
15
27
|
* no Default, and a value that is not a hosted speaker is ignored.
|