@burdenoff/website-sdk 2026.1104.3 → 2026.1105.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/README.md +51 -6
- package/dist/avatar-live-view/index.d.mts +1 -1
- package/dist/avatar-live-view/index.d.ts +1 -1
- package/dist/avatar-live-view/index.js +482 -64
- package/dist/avatar-live-view/index.js.map +1 -1
- package/dist/avatar-live-view/index.mjs +476 -58
- package/dist/avatar-live-view/index.mjs.map +1 -1
- package/dist/avatar-mic/index.d.mts +193 -0
- package/dist/avatar-mic/index.d.ts +193 -0
- package/dist/avatar-mic/index.js +738 -0
- package/dist/avatar-mic/index.js.map +1 -0
- package/dist/avatar-mic/index.mjs +736 -0
- package/dist/avatar-mic/index.mjs.map +1 -0
- package/dist/{chunk-5JQGE3XI.mjs → chunk-7HE33NBS.mjs} +4 -111
- package/dist/chunk-7HE33NBS.mjs.map +1 -0
- package/dist/chunk-JZS6FQT6.mjs +127 -0
- package/dist/chunk-JZS6FQT6.mjs.map +1 -0
- package/dist/{chunk-22OT6E26.js → chunk-MBVXDFVC.js} +18 -127
- package/dist/chunk-MBVXDFVC.js.map +1 -0
- package/dist/{chunk-7MSB3EUI.mjs → chunk-MVNN6LLO.mjs} +4 -3
- package/dist/chunk-MVNN6LLO.mjs.map +1 -0
- package/dist/chunk-N7E6Z2GF.js +132 -0
- package/dist/chunk-N7E6Z2GF.js.map +1 -0
- package/dist/{chunk-WSEQ2I6I.js → chunk-XIZE37IB.js} +4 -3
- package/dist/chunk-XIZE37IB.js.map +1 -0
- package/dist/components/explore.d.mts +1 -1
- package/dist/components/explore.d.ts +1 -1
- package/dist/components/explore.js +20 -20
- package/dist/components/explore.mjs +2 -2
- package/dist/{explore-BV4dRwoC.d.ts → explore-Dfru4j4m.d.ts} +45 -31
- package/dist/{explore-avatar-live-config-DRpS-mqQ.d.ts → explore-avatar-live-config-C-OwXNje.d.ts} +8 -0
- package/dist/{explore-avatar-live-config-B6_p0q1c.d.mts → explore-avatar-live-config-OO0hvQCK.d.mts} +8 -0
- package/dist/{explore-BgEeGPvY.d.mts → explore-ei8uoko9.d.mts} +45 -31
- package/dist/index.d.mts +11 -6
- package/dist/index.d.ts +11 -6
- package/dist/index.js +70 -68
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +9 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-22OT6E26.js.map +0 -1
- package/dist/chunk-5JQGE3XI.mjs.map +0 -1
- package/dist/chunk-7MSB3EUI.mjs.map +0 -1
- package/dist/chunk-O4GS3XPZ.js +0 -10
- package/dist/chunk-O4GS3XPZ.js.map +0 -1
- package/dist/chunk-TT3MHN42.mjs +0 -8
- package/dist/chunk-TT3MHN42.mjs.map +0 -1
- package/dist/chunk-WSEQ2I6I.js.map +0 -1
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
3
|
+
var chunkXIZE37IB_js = require('../chunk-XIZE37IB.js');
|
|
4
|
+
var chunkN7E6Z2GF_js = require('../chunk-N7E6Z2GF.js');
|
|
5
5
|
var chunkC6OJIHNO_js = require('../chunk-C6OJIHNO.js');
|
|
6
6
|
require('../chunk-ZSTFX7FV.js');
|
|
7
7
|
var react = require('react');
|
|
8
|
+
var lucideReact = require('lucide-react');
|
|
8
9
|
var jsxRuntime = require('react/jsx-runtime');
|
|
9
10
|
|
|
10
11
|
// src/components/explore-avatar-api.ts
|
|
11
12
|
var AVAILABILITY_TIMEOUT_MS = 8e3;
|
|
12
13
|
var CREATE_TIMEOUT_MS = 3e4;
|
|
13
14
|
var MESSAGE_ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
15
|
+
var STT_TOKEN = /^[A-Za-z0-9._~-]{1,4096}$/;
|
|
14
16
|
var CONVERSATION_TOKEN = /^[A-Za-z0-9._~+/=-]{16,512}$/;
|
|
15
17
|
var LOCALE = /^[A-Za-z0-9-]{1,16}$/;
|
|
16
18
|
var SESSION_ID = /^avs_[0-9a-f]{32}$/;
|
|
@@ -58,6 +60,15 @@ function readQuestions(value) {
|
|
|
58
60
|
if (typeof value !== "string" || !QUESTION_MODES.has(value)) return null;
|
|
59
61
|
return value === "any" ? "any" : "suggested";
|
|
60
62
|
}
|
|
63
|
+
function readSttOffer(value) {
|
|
64
|
+
if (!isRecord(value)) return void 0;
|
|
65
|
+
const keys = Object.keys(value).sort();
|
|
66
|
+
const { token, expiresAt } = value;
|
|
67
|
+
if (keys.length !== 2 || keys[0] !== "expiresAt" || keys[1] !== "token" || typeof token !== "string" || !STT_TOKEN.test(token) || typeof expiresAt !== "number" || !Number.isSafeInteger(expiresAt) || expiresAt <= 0) {
|
|
68
|
+
return void 0;
|
|
69
|
+
}
|
|
70
|
+
return { token, expiresAt };
|
|
71
|
+
}
|
|
61
72
|
function readAvailability(status, body) {
|
|
62
73
|
if (status !== 200 || !isRecord(body)) return INERT;
|
|
63
74
|
const { available, reason } = body;
|
|
@@ -68,12 +79,19 @@ function readAvailability(status, body) {
|
|
|
68
79
|
const { consentVersion, maxSessionSeconds } = body;
|
|
69
80
|
const botCheck = readBotCheck(body.botCheck);
|
|
70
81
|
const questions = readQuestions(body.questions);
|
|
71
|
-
if (available !== true || typeof consentVersion !== "string" || !
|
|
82
|
+
if (available !== true || typeof consentVersion !== "string" || !chunkXIZE37IB_js.isAvatarVersion(consentVersion) || typeof maxSessionSeconds !== "number" || !Number.isInteger(maxSessionSeconds) || maxSessionSeconds < 1 || botCheck === null || questions === null) {
|
|
72
83
|
return INERT;
|
|
73
84
|
}
|
|
85
|
+
const stt = readSttOffer(body.stt);
|
|
74
86
|
return {
|
|
75
87
|
kind: "available",
|
|
76
|
-
offer: {
|
|
88
|
+
offer: {
|
|
89
|
+
consentVersion,
|
|
90
|
+
maxSessionSeconds,
|
|
91
|
+
botCheck,
|
|
92
|
+
questions,
|
|
93
|
+
...stt === void 0 ? {} : { stt }
|
|
94
|
+
}
|
|
77
95
|
};
|
|
78
96
|
}
|
|
79
97
|
const meaning = available ? void 0 : AVAILABILITY_REASONS.get(reason);
|
|
@@ -161,7 +179,14 @@ function deadline(timeoutMs, outer) {
|
|
|
161
179
|
};
|
|
162
180
|
}
|
|
163
181
|
async function requestAvailability(target) {
|
|
164
|
-
|
|
182
|
+
try {
|
|
183
|
+
return await requestAvailabilityAnswer(target);
|
|
184
|
+
} catch {
|
|
185
|
+
return INERT;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async function requestAvailabilityAnswer(target) {
|
|
189
|
+
if (!chunkXIZE37IB_js.isAvatarSite(target.site) || !LOCALE.test(target.locale)) return INERT;
|
|
165
190
|
const query = new URLSearchParams({
|
|
166
191
|
site: target.site,
|
|
167
192
|
locale: target.locale
|
|
@@ -176,13 +201,11 @@ async function requestAvailability(target) {
|
|
|
176
201
|
signal: timer.signal
|
|
177
202
|
});
|
|
178
203
|
return readAvailability(response.status, await readJson(response));
|
|
179
|
-
} catch {
|
|
180
|
-
return INERT;
|
|
181
204
|
} finally {
|
|
182
205
|
timer.done();
|
|
183
206
|
}
|
|
184
207
|
}
|
|
185
|
-
|
|
208
|
+
function requestCreate(target) {
|
|
186
209
|
const { body } = target;
|
|
187
210
|
const payload = JSON.stringify({
|
|
188
211
|
site: body.site,
|
|
@@ -192,27 +215,45 @@ async function requestCreate(target) {
|
|
|
192
215
|
consentVersion: body.consentVersion,
|
|
193
216
|
manifestVersion: body.manifestVersion
|
|
194
217
|
});
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
218
|
+
const controller = new AbortController();
|
|
219
|
+
let answered = false;
|
|
220
|
+
const answer = (async () => {
|
|
221
|
+
try {
|
|
222
|
+
const response = await fetch(`${target.base}/sessions`, {
|
|
223
|
+
method: "POST",
|
|
224
|
+
credentials: "omit",
|
|
225
|
+
cache: "no-store",
|
|
226
|
+
headers: {
|
|
227
|
+
...target.headers,
|
|
228
|
+
"Content-Type": "application/json",
|
|
229
|
+
"Idempotency-Key": target.idempotencyKey,
|
|
230
|
+
...target.botToken === void 0 ? {} : { "x-web-avatar-bot-token": target.botToken }
|
|
231
|
+
},
|
|
232
|
+
body: payload,
|
|
233
|
+
signal: controller.signal
|
|
234
|
+
});
|
|
235
|
+
answered = true;
|
|
236
|
+
return readCreate(response.status, await readJson(response));
|
|
237
|
+
} catch {
|
|
238
|
+
return FAILED;
|
|
239
|
+
}
|
|
240
|
+
})();
|
|
241
|
+
return new Promise((resolve) => {
|
|
242
|
+
let late = false;
|
|
243
|
+
const timer = setTimeout(() => {
|
|
244
|
+
late = true;
|
|
245
|
+
if (!answered) controller.abort();
|
|
246
|
+
resolve(FAILED);
|
|
247
|
+
}, CREATE_TIMEOUT_MS);
|
|
248
|
+
void answer.then((result) => {
|
|
249
|
+
if (late) {
|
|
250
|
+
target.onLate?.(result);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
clearTimeout(timer);
|
|
254
|
+
resolve(result);
|
|
209
255
|
});
|
|
210
|
-
|
|
211
|
-
} catch {
|
|
212
|
-
return INERT;
|
|
213
|
-
} finally {
|
|
214
|
-
timer.done();
|
|
215
|
-
}
|
|
256
|
+
});
|
|
216
257
|
}
|
|
217
258
|
function sendEnd(target) {
|
|
218
259
|
try {
|
|
@@ -785,9 +826,321 @@ var CONSENT = /* @__PURE__ */ new Map([
|
|
|
785
826
|
]
|
|
786
827
|
]);
|
|
787
828
|
function avatarConsentCopy(version, locale) {
|
|
788
|
-
if (!
|
|
829
|
+
if (!chunkXIZE37IB_js.isAvatarLiveLocaleReviewed(locale)) return null;
|
|
789
830
|
return CONSENT.get(version)?.[locale] ?? null;
|
|
790
831
|
}
|
|
832
|
+
var MIC_EN = {
|
|
833
|
+
micStart: "Ask by voice",
|
|
834
|
+
micStop: "Stop recording",
|
|
835
|
+
micAllow: "Use the microphone",
|
|
836
|
+
micNotNow: "Not now",
|
|
837
|
+
micStarting: "Starting the microphone\u2026",
|
|
838
|
+
micListening: "Listening\u2026",
|
|
839
|
+
micElapsed: "{elapsed} of {limit}",
|
|
840
|
+
micFinishing: "Getting the text\u2026",
|
|
841
|
+
micDone: "Check the text, then press Send.",
|
|
842
|
+
micDoneLimit: "The recording stopped at thirty seconds. Check the text, then press Send.",
|
|
843
|
+
micNoSpeech: "No words were heard. Try again, or type your question.",
|
|
844
|
+
micDenied: "The microphone is blocked in this browser. You can type your question instead.",
|
|
845
|
+
micUnsupported: "The microphone can't be used here. You can type your question instead.",
|
|
846
|
+
micUnavailable: "Voice questions aren't available right now. You can type your question instead.",
|
|
847
|
+
micLimit: "Voice questions have reached their limit for now. You can type your question instead.",
|
|
848
|
+
micBusy: "Voice questions are busy right now. Try again in a moment, or type your question.",
|
|
849
|
+
micFailed: "The microphone stopped before the text came. Try again, or type your question."
|
|
850
|
+
};
|
|
851
|
+
var MIC_STRINGS = { en: MIC_EN };
|
|
852
|
+
function avatarMicStrings(locale) {
|
|
853
|
+
if (!chunkXIZE37IB_js.isAvatarLiveLocaleReviewed(locale)) return null;
|
|
854
|
+
return MIC_STRINGS[locale] ?? null;
|
|
855
|
+
}
|
|
856
|
+
var AVATAR_MIC_NOTICE_VERSION = "web-mic-2026-11-05";
|
|
857
|
+
var MIC_NOTICE = /* @__PURE__ */ new Map([
|
|
858
|
+
[
|
|
859
|
+
AVATAR_MIC_NOTICE_VERSION,
|
|
860
|
+
{
|
|
861
|
+
en: {
|
|
862
|
+
title: "Ask with your voice",
|
|
863
|
+
processing: "While you speak, your voice is streamed to our speech service, and Google Cloud Speech-to-Text turns it into text in Google's EU region.",
|
|
864
|
+
storage: "We don't store or log your audio, or the text made from it.",
|
|
865
|
+
text: "The text appears in the box and stays in this browser until you press Send. Nothing is sent by itself. Once you send it, it is a chat message, kept like any other.",
|
|
866
|
+
limits: "Each recording stops after thirty seconds, and voice questions have daily limits.",
|
|
867
|
+
privacyLink: "How we handle this"
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
]
|
|
871
|
+
]);
|
|
872
|
+
function avatarMicNotice(version, locale) {
|
|
873
|
+
if (!chunkXIZE37IB_js.isAvatarLiveLocaleReviewed(locale)) return null;
|
|
874
|
+
return MIC_NOTICE.get(version)?.[locale] ?? null;
|
|
875
|
+
}
|
|
876
|
+
var PRIVACY_HREF = "/privacy#live-avatar";
|
|
877
|
+
var LIMIT_SECONDS = 30;
|
|
878
|
+
var TICK_MS = 250;
|
|
879
|
+
var IDLE = { name: "idle" };
|
|
880
|
+
var FOR_GOOD = /* @__PURE__ */ new Set([
|
|
881
|
+
"unavailable",
|
|
882
|
+
"limit"
|
|
883
|
+
]);
|
|
884
|
+
var FAILURE_TEXT = {
|
|
885
|
+
denied: "micDenied",
|
|
886
|
+
unsupported: "micUnsupported",
|
|
887
|
+
unavailable: "micUnavailable",
|
|
888
|
+
limit: "micLimit",
|
|
889
|
+
busy: "micBusy",
|
|
890
|
+
failed: "micFailed"
|
|
891
|
+
};
|
|
892
|
+
function loadAvatarMic() {
|
|
893
|
+
return import('../avatar-mic/index.js').then((loaded) => ({
|
|
894
|
+
createAvatarMic: loaded.createAvatarMic
|
|
895
|
+
}));
|
|
896
|
+
}
|
|
897
|
+
function canUseAvatarMic() {
|
|
898
|
+
return typeof navigator !== "undefined" && typeof navigator.mediaDevices?.getUserMedia === "function" && typeof AudioContext !== "undefined" && typeof AudioWorkletNode !== "undefined" && typeof WebSocket !== "undefined";
|
|
899
|
+
}
|
|
900
|
+
function clock(seconds) {
|
|
901
|
+
const whole = Math.max(0, Math.floor(seconds));
|
|
902
|
+
return `${Math.floor(whole / 60)}:${String(whole % 60).padStart(2, "0")}`;
|
|
903
|
+
}
|
|
904
|
+
function withHeardText(current, heard, maxLength) {
|
|
905
|
+
const text = heard.trim();
|
|
906
|
+
if (text === "") return current;
|
|
907
|
+
const joined = current.trim() === "" ? text : `${current.replace(/\s+$/, "")} ${text}`;
|
|
908
|
+
return joined.slice(0, Math.max(0, maxLength));
|
|
909
|
+
}
|
|
910
|
+
function useAvatarMic({
|
|
911
|
+
enabled,
|
|
912
|
+
locale,
|
|
913
|
+
base,
|
|
914
|
+
offer,
|
|
915
|
+
refreshOffer,
|
|
916
|
+
onText
|
|
917
|
+
}) {
|
|
918
|
+
const S = avatarMicStrings(locale);
|
|
919
|
+
const copy = avatarMicNotice(AVATAR_MIC_NOTICE_VERSION, locale);
|
|
920
|
+
const uid = react.useId();
|
|
921
|
+
const noticeTitleId = `${uid}-mic-notice`;
|
|
922
|
+
const [withdrawn, setWithdrawn] = react.useState(null);
|
|
923
|
+
const [supported, setSupported] = react.useState(false);
|
|
924
|
+
react.useEffect(() => setSupported(canUseAvatarMic()), []);
|
|
925
|
+
const possible = enabled && supported && base !== null && S !== null && copy !== null;
|
|
926
|
+
const offered = possible && withdrawn === null;
|
|
927
|
+
const [state, setState] = react.useState(IDLE);
|
|
928
|
+
const [noticeOpen, setNoticeOpen] = react.useState(false);
|
|
929
|
+
const agreedRef = react.useRef(false);
|
|
930
|
+
const [loading2, setLoading] = react.useState(false);
|
|
931
|
+
const [loadFailed, setLoadFailed] = react.useState(false);
|
|
932
|
+
const [now, setNow] = react.useState(() => Date.now());
|
|
933
|
+
const micRef = react.useRef(null);
|
|
934
|
+
const seqRef = react.useRef(0);
|
|
935
|
+
const buttonRef = react.useRef(null);
|
|
936
|
+
const allowRef = react.useRef(null);
|
|
937
|
+
const usedEpochRef = react.useRef(0);
|
|
938
|
+
const latest = react.useRef({ offer, refreshOffer, onText, base });
|
|
939
|
+
latest.current = { offer, refreshOffer, onText, base };
|
|
940
|
+
const cancel = react.useCallback(() => {
|
|
941
|
+
seqRef.current += 1;
|
|
942
|
+
setLoading(false);
|
|
943
|
+
setLoadFailed(false);
|
|
944
|
+
setNoticeOpen(false);
|
|
945
|
+
micRef.current?.mic.cancel();
|
|
946
|
+
}, []);
|
|
947
|
+
react.useEffect(() => {
|
|
948
|
+
if (!offered) cancel();
|
|
949
|
+
}, [offered, cancel]);
|
|
950
|
+
react.useEffect(
|
|
951
|
+
() => () => {
|
|
952
|
+
seqRef.current += 1;
|
|
953
|
+
micRef.current?.mic.cancel();
|
|
954
|
+
},
|
|
955
|
+
[]
|
|
956
|
+
);
|
|
957
|
+
const micFor = react.useCallback(async (target) => {
|
|
958
|
+
const held = micRef.current;
|
|
959
|
+
if (held !== null && held.base === target) return held.mic;
|
|
960
|
+
held?.mic.cancel();
|
|
961
|
+
const loaded = await loadAvatarMic();
|
|
962
|
+
const mic = loaded.createAvatarMic({
|
|
963
|
+
base: target,
|
|
964
|
+
notice: AVATAR_MIC_NOTICE_VERSION,
|
|
965
|
+
offer: () => latest.current.offer,
|
|
966
|
+
refreshOffer: () => latest.current.refreshOffer(),
|
|
967
|
+
onState: setState
|
|
968
|
+
});
|
|
969
|
+
micRef.current = { base: target, mic };
|
|
970
|
+
return mic;
|
|
971
|
+
}, []);
|
|
972
|
+
const press = react.useCallback(() => {
|
|
973
|
+
const target = latest.current.base;
|
|
974
|
+
if (target === null) return;
|
|
975
|
+
seqRef.current += 1;
|
|
976
|
+
const seq = seqRef.current;
|
|
977
|
+
setLoadFailed(false);
|
|
978
|
+
setLoading(true);
|
|
979
|
+
void micFor(target).then(
|
|
980
|
+
(mic) => {
|
|
981
|
+
if (seqRef.current !== seq) return;
|
|
982
|
+
setLoading(false);
|
|
983
|
+
mic.start();
|
|
984
|
+
},
|
|
985
|
+
() => {
|
|
986
|
+
if (seqRef.current !== seq) return;
|
|
987
|
+
setLoading(false);
|
|
988
|
+
setLoadFailed(true);
|
|
989
|
+
}
|
|
990
|
+
);
|
|
991
|
+
}, [micFor]);
|
|
992
|
+
const recording = loading2 || state.name === "starting" || state.name === "listening";
|
|
993
|
+
const tap = react.useCallback(() => {
|
|
994
|
+
if (state.name === "listening" || state.name === "starting") {
|
|
995
|
+
micRef.current?.mic.stop();
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
if (loading2) {
|
|
999
|
+
cancel();
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
if (!agreedRef.current) {
|
|
1003
|
+
setNoticeOpen(true);
|
|
1004
|
+
void loadAvatarMic().catch(() => void 0);
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
press();
|
|
1008
|
+
}, [cancel, loading2, press, state.name]);
|
|
1009
|
+
const allow = react.useCallback(() => {
|
|
1010
|
+
agreedRef.current = true;
|
|
1011
|
+
setNoticeOpen(false);
|
|
1012
|
+
buttonRef.current?.focus();
|
|
1013
|
+
press();
|
|
1014
|
+
}, [press]);
|
|
1015
|
+
const notNow = react.useCallback(() => {
|
|
1016
|
+
setNoticeOpen(false);
|
|
1017
|
+
buttonRef.current?.focus();
|
|
1018
|
+
}, []);
|
|
1019
|
+
react.useEffect(() => {
|
|
1020
|
+
if (noticeOpen) allowRef.current?.focus();
|
|
1021
|
+
}, [noticeOpen]);
|
|
1022
|
+
const listeningSince = state.name === "listening" ? state.startedAt : null;
|
|
1023
|
+
react.useEffect(() => {
|
|
1024
|
+
if (listeningSince === null) return;
|
|
1025
|
+
setNow(Date.now());
|
|
1026
|
+
const timer = setInterval(() => setNow(Date.now()), TICK_MS);
|
|
1027
|
+
return () => clearInterval(timer);
|
|
1028
|
+
}, [listeningSince]);
|
|
1029
|
+
react.useEffect(() => {
|
|
1030
|
+
if (state.name === "done" && state.epoch !== usedEpochRef.current) {
|
|
1031
|
+
usedEpochRef.current = state.epoch;
|
|
1032
|
+
if (state.text !== "") latest.current.onText(state.text);
|
|
1033
|
+
} else if (state.name === "failed" && FOR_GOOD.has(state.reason)) {
|
|
1034
|
+
setWithdrawn(state.reason);
|
|
1035
|
+
}
|
|
1036
|
+
}, [state]);
|
|
1037
|
+
if (!possible || S === null || copy === null) {
|
|
1038
|
+
return { button: null, notice: null, lines: null, cancel };
|
|
1039
|
+
}
|
|
1040
|
+
const status = withdrawn !== null ? S[FAILURE_TEXT[withdrawn]] : loading2 ? S.micStarting : loadFailed ? S.micFailed : state.name === "starting" ? S.micStarting : state.name === "listening" ? S.micListening : state.name === "finishing" ? S.micFinishing : state.name === "done" ? state.text === "" ? S.micNoSpeech : state.limit ? S.micDoneLimit : S.micDone : state.name === "failed" ? S[FAILURE_TEXT[state.reason]] : "";
|
|
1041
|
+
const heard = offered && (state.name === "listening" || state.name === "finishing") ? state.text : "";
|
|
1042
|
+
const button = !offered ? null : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1043
|
+
"button",
|
|
1044
|
+
{
|
|
1045
|
+
ref: buttonRef,
|
|
1046
|
+
type: "button",
|
|
1047
|
+
"aria-label": recording ? S.micStop : S.micStart,
|
|
1048
|
+
"aria-pressed": recording,
|
|
1049
|
+
title: recording ? S.micStop : S.micStart,
|
|
1050
|
+
"data-boff-explore": "avatar-live-mic",
|
|
1051
|
+
"data-mic": loading2 ? "starting" : state.name,
|
|
1052
|
+
className: chunkC6OJIHNO_js.cn(
|
|
1053
|
+
"inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-md border transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background motion-reduce:transition-none",
|
|
1054
|
+
recording ? "border-primary bg-primary text-primary-foreground" : "border-border bg-background text-foreground hover:bg-accent hover:text-accent-foreground"
|
|
1055
|
+
),
|
|
1056
|
+
onClick: tap,
|
|
1057
|
+
children: recording ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Square, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Mic, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
1058
|
+
}
|
|
1059
|
+
);
|
|
1060
|
+
const notice = offered && noticeOpen ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1061
|
+
"section",
|
|
1062
|
+
{
|
|
1063
|
+
"aria-labelledby": noticeTitleId,
|
|
1064
|
+
"data-boff-explore": "avatar-live-mic-notice",
|
|
1065
|
+
className: "w-full rounded-lg border border-border p-3 text-start",
|
|
1066
|
+
children: [
|
|
1067
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { id: noticeTitleId, className: "text-sm font-semibold", children: copy.title }),
|
|
1068
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: copy.processing }),
|
|
1069
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: copy.storage }),
|
|
1070
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: copy.text }),
|
|
1071
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: copy.limits }),
|
|
1072
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1073
|
+
"a",
|
|
1074
|
+
{
|
|
1075
|
+
href: PRIVACY_HREF,
|
|
1076
|
+
target: "_blank",
|
|
1077
|
+
rel: "noopener noreferrer",
|
|
1078
|
+
"data-boff-explore": "avatar-live-mic-privacy",
|
|
1079
|
+
className: "mt-1 inline-block text-xs font-medium text-primary underline underline-offset-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
1080
|
+
children: copy.privacyLink
|
|
1081
|
+
}
|
|
1082
|
+
),
|
|
1083
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex flex-wrap gap-2", children: [
|
|
1084
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1085
|
+
"button",
|
|
1086
|
+
{
|
|
1087
|
+
ref: allowRef,
|
|
1088
|
+
type: "button",
|
|
1089
|
+
"data-boff-explore": "avatar-live-mic-allow",
|
|
1090
|
+
className: "inline-flex min-h-11 items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background motion-reduce:transition-none",
|
|
1091
|
+
onClick: allow,
|
|
1092
|
+
children: S.micAllow
|
|
1093
|
+
}
|
|
1094
|
+
),
|
|
1095
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1096
|
+
"button",
|
|
1097
|
+
{
|
|
1098
|
+
type: "button",
|
|
1099
|
+
"data-boff-explore": "avatar-live-mic-not-now",
|
|
1100
|
+
className: "inline-flex min-h-11 items-center justify-center rounded-md border border-border bg-background px-4 py-2 text-sm font-medium text-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background motion-reduce:transition-none",
|
|
1101
|
+
onClick: notNow,
|
|
1102
|
+
children: S.micNotNow
|
|
1103
|
+
}
|
|
1104
|
+
)
|
|
1105
|
+
] })
|
|
1106
|
+
]
|
|
1107
|
+
}
|
|
1108
|
+
) : null;
|
|
1109
|
+
const listening = offered && state.name === "listening";
|
|
1110
|
+
const lines = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1111
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
1112
|
+
listening ? /* @__PURE__ */ jsxRuntime.jsxs("span", { "aria-hidden": "true", className: "relative flex h-2 w-2 shrink-0", children: [
|
|
1113
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-primary opacity-75 motion-reduce:animate-none" }),
|
|
1114
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative inline-flex h-2 w-2 rounded-full bg-primary" })
|
|
1115
|
+
] }) : null,
|
|
1116
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { role: "status", "data-boff-explore": "avatar-live-mic-status", children: status }),
|
|
1117
|
+
listening ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1118
|
+
"span",
|
|
1119
|
+
{
|
|
1120
|
+
role: "timer",
|
|
1121
|
+
"data-boff-explore": "avatar-live-mic-elapsed",
|
|
1122
|
+
className: "tabular-nums",
|
|
1123
|
+
children: chunkN7E6Z2GF_js.fill(S.micElapsed, {
|
|
1124
|
+
elapsed: clock(
|
|
1125
|
+
Math.min(LIMIT_SECONDS, (now - (listeningSince ?? now)) / 1e3)
|
|
1126
|
+
),
|
|
1127
|
+
limit: clock(LIMIT_SECONDS)
|
|
1128
|
+
})
|
|
1129
|
+
}
|
|
1130
|
+
) : null
|
|
1131
|
+
] }),
|
|
1132
|
+
heard === "" ? null : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1133
|
+
"p",
|
|
1134
|
+
{
|
|
1135
|
+
"data-boff-explore": "avatar-live-mic-heard",
|
|
1136
|
+
dir: "auto",
|
|
1137
|
+
className: "line-clamp-3 break-words text-sm italic text-muted-foreground",
|
|
1138
|
+
children: heard
|
|
1139
|
+
}
|
|
1140
|
+
)
|
|
1141
|
+
] });
|
|
1142
|
+
return { button, notice, lines, cancel };
|
|
1143
|
+
}
|
|
791
1144
|
|
|
792
1145
|
// src/components/explore-avatar-turnstile.ts
|
|
793
1146
|
var SCRIPT_SRC = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
|
|
@@ -903,7 +1256,7 @@ function runTurnstile(container, siteKey, action, cData) {
|
|
|
903
1256
|
}
|
|
904
1257
|
};
|
|
905
1258
|
}
|
|
906
|
-
var
|
|
1259
|
+
var PRIVACY_HREF2 = "/privacy#live-avatar";
|
|
907
1260
|
var SPEAKER_WAIT_MS = 3e4;
|
|
908
1261
|
var END_GRACE_MS = 1e4;
|
|
909
1262
|
var CLOCK_TOLERANCE_MS = 5e3;
|
|
@@ -916,7 +1269,7 @@ var ASK_MAX_LENGTH = 300;
|
|
|
916
1269
|
var MAX_REFERENCES = 64;
|
|
917
1270
|
var USED_ON_KEY = "boff.explore.avatarUsedOn";
|
|
918
1271
|
var PRIMARY = '[data-avatar-primary], [data-boff-explore="panel-continue-chat"]';
|
|
919
|
-
var
|
|
1272
|
+
var IDLE2 = { name: "idle" };
|
|
920
1273
|
var INERT2 = { name: "inert" };
|
|
921
1274
|
function stopAttending(handle) {
|
|
922
1275
|
if (handle.unheard !== null) clearTimeout(handle.unheard);
|
|
@@ -1006,6 +1359,12 @@ function firstExchange(messages, rules = SUGGESTED_RULES) {
|
|
|
1006
1359
|
function inFlight(conversation) {
|
|
1007
1360
|
return conversation?.phase === "sending" || conversation?.phase === "streaming";
|
|
1008
1361
|
}
|
|
1362
|
+
function acceptsQuestion(slot) {
|
|
1363
|
+
return slot.any && !slot.textMode && slot.phase === "needs-answer" && slot.kind === "empty" && slot.reason === null;
|
|
1364
|
+
}
|
|
1365
|
+
function micOfferOf(result) {
|
|
1366
|
+
return result.kind === "available" && result.offer.stt !== void 0 ? { ...result.offer.stt, receivedAt: Date.now() } : null;
|
|
1367
|
+
}
|
|
1009
1368
|
function utcToday() {
|
|
1010
1369
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1011
1370
|
}
|
|
@@ -1075,7 +1434,7 @@ async function loadModule(load) {
|
|
|
1075
1434
|
}
|
|
1076
1435
|
return loaded;
|
|
1077
1436
|
}
|
|
1078
|
-
function
|
|
1437
|
+
function clock2(seconds) {
|
|
1079
1438
|
const whole = Math.max(0, Math.ceil(seconds));
|
|
1080
1439
|
return `${Math.floor(whole / 60)}:${String(whole % 60).padStart(2, "0")}`;
|
|
1081
1440
|
}
|
|
@@ -1128,7 +1487,7 @@ function AvatarLiveView({
|
|
|
1128
1487
|
const shown = offeredAt(exchange, Date.now());
|
|
1129
1488
|
const { answerId, startOver } = shown;
|
|
1130
1489
|
const offerUntil = answerId === null ? null : exchange.offerUntil;
|
|
1131
|
-
const [phase, setPhase] = react.useState(
|
|
1490
|
+
const [phase, setPhase] = react.useState(IDLE2);
|
|
1132
1491
|
const [blocked, setBlocked] = react.useState(false);
|
|
1133
1492
|
const [now, setNow] = react.useState(() => Date.now());
|
|
1134
1493
|
const [draft, setDraft] = react.useState("");
|
|
@@ -1138,7 +1497,7 @@ function AvatarLiveView({
|
|
|
1138
1497
|
const [usedToday, setUsedToday] = react.useState(false);
|
|
1139
1498
|
const showing = active && !off;
|
|
1140
1499
|
const behindChat = open && !active && !off;
|
|
1141
|
-
const phaseRef = react.useRef(
|
|
1500
|
+
const phaseRef = react.useRef(IDLE2);
|
|
1142
1501
|
const latest = react.useRef({
|
|
1143
1502
|
live,
|
|
1144
1503
|
locale,
|
|
@@ -1154,6 +1513,8 @@ function AvatarLiveView({
|
|
|
1154
1513
|
const waitRef = react.useRef(null);
|
|
1155
1514
|
const sendingRef = react.useRef(false);
|
|
1156
1515
|
const textBoxRef = react.useRef(false);
|
|
1516
|
+
const [micOffer, setMicOffer] = react.useState(null);
|
|
1517
|
+
const micCancelRef = react.useRef(() => void 0);
|
|
1157
1518
|
const checkRef = react.useRef(null);
|
|
1158
1519
|
const askedLocale = react.useRef(locale);
|
|
1159
1520
|
const startRef = react.useRef(null);
|
|
@@ -1274,6 +1635,7 @@ function AvatarLiveView({
|
|
|
1274
1635
|
const applyAvailability = react.useCallback(
|
|
1275
1636
|
(result, locale2) => {
|
|
1276
1637
|
const { live: live2 } = latest.current;
|
|
1638
|
+
setMicOffer(micOfferOf(result));
|
|
1277
1639
|
if (result.kind === "inert") {
|
|
1278
1640
|
setMode("suggested");
|
|
1279
1641
|
go(INERT2);
|
|
@@ -1291,7 +1653,7 @@ function AvatarLiveView({
|
|
|
1291
1653
|
go(INERT2);
|
|
1292
1654
|
return;
|
|
1293
1655
|
}
|
|
1294
|
-
if (result.offer.botCheck === "turnstile" &&
|
|
1656
|
+
if (result.offer.botCheck === "turnstile" && chunkXIZE37IB_js.turnstileSiteKeyOf(live2) === null) {
|
|
1295
1657
|
setMode("suggested");
|
|
1296
1658
|
go(INERT2);
|
|
1297
1659
|
return;
|
|
@@ -1361,21 +1723,21 @@ function AvatarLiveView({
|
|
|
1361
1723
|
[go]
|
|
1362
1724
|
);
|
|
1363
1725
|
const settle = react.useCallback(
|
|
1364
|
-
(result,
|
|
1726
|
+
(result, givenUp, context) => {
|
|
1365
1727
|
const created = result.kind === "created" ? {
|
|
1366
1728
|
sessionId: result.session.sessionId,
|
|
1367
1729
|
secret: result.session.endSecret
|
|
1368
1730
|
} : result.kind === "unusable" ? { sessionId: result.sessionId, secret: result.endSecret } : null;
|
|
1369
1731
|
const claimed = created !== null || result.kind === "refused" && result.reason === "answer_claimed";
|
|
1370
1732
|
if (claimed) claimedRef.current.add(context.messageId);
|
|
1371
|
-
if (
|
|
1733
|
+
if (givenUp !== null) {
|
|
1372
1734
|
if (created !== null) {
|
|
1373
1735
|
sendEnd({
|
|
1374
1736
|
base: context.base,
|
|
1375
1737
|
headers: context.headers,
|
|
1376
1738
|
sessionId: created.sessionId,
|
|
1377
1739
|
secret: created.secret,
|
|
1378
|
-
reason:
|
|
1740
|
+
reason: givenUp
|
|
1379
1741
|
});
|
|
1380
1742
|
}
|
|
1381
1743
|
const shown2 = phaseRef.current;
|
|
@@ -1428,7 +1790,7 @@ function AvatarLiveView({
|
|
|
1428
1790
|
return;
|
|
1429
1791
|
}
|
|
1430
1792
|
const refusedFrom = exchangeAtStart.offerUntil - ANSWER_OFFER_MS + SERVICE_MAX_AGE_MS;
|
|
1431
|
-
const siteKey = offer.botCheck === "turnstile" ?
|
|
1793
|
+
const siteKey = offer.botCheck === "turnstile" ? chunkXIZE37IB_js.turnstileSiteKeyOf(live2) : null;
|
|
1432
1794
|
if (offer.botCheck === "turnstile" && (siteKey === null || !canBindBotCheck())) {
|
|
1433
1795
|
go(INERT2);
|
|
1434
1796
|
return;
|
|
@@ -1484,21 +1846,21 @@ function AvatarLiveView({
|
|
|
1484
1846
|
return;
|
|
1485
1847
|
}
|
|
1486
1848
|
if (turnstile !== null) go({ name: "creating" });
|
|
1849
|
+
const context = { module: loaded, offer, messageId, base: base2, headers: headers2 };
|
|
1487
1850
|
const result = await requestCreate({
|
|
1488
1851
|
base: base2,
|
|
1489
1852
|
headers: headers2,
|
|
1490
1853
|
idempotencyKey,
|
|
1491
1854
|
botToken,
|
|
1492
|
-
body: body2
|
|
1855
|
+
body: body2,
|
|
1856
|
+
// An answer past the deadline, after the view has said the live avatar couldn't start: a session it made is
|
|
1857
|
+
// ended at once with its own secret, and never joined. The reason is the visitor's own where they gave the
|
|
1858
|
+
// start up first, else `client_error`. It reads `run` and never writes it, so the deadline's `failed` below is
|
|
1859
|
+
// shown whichever of the two is handled first.
|
|
1860
|
+
onLate: (late) => settle(late, run.cancelled ?? "client_error", context)
|
|
1493
1861
|
});
|
|
1494
1862
|
if (startRef.current === run) startRef.current = null;
|
|
1495
|
-
settle(result, run,
|
|
1496
|
-
module: loaded,
|
|
1497
|
-
offer,
|
|
1498
|
-
messageId,
|
|
1499
|
-
base: base2,
|
|
1500
|
-
headers: headers2
|
|
1501
|
-
});
|
|
1863
|
+
settle(result, run.cancelled, context);
|
|
1502
1864
|
})();
|
|
1503
1865
|
},
|
|
1504
1866
|
[exchangeNow, go, reoffer, settle]
|
|
@@ -1589,6 +1951,7 @@ function AvatarLiveView({
|
|
|
1589
1951
|
[applyAvailability, bindingHolds, cancelWaiting, exchangeNow, launch]
|
|
1590
1952
|
);
|
|
1591
1953
|
const stop = react.useCallback(() => {
|
|
1954
|
+
micCancelRef.current();
|
|
1592
1955
|
const current = phaseRef.current;
|
|
1593
1956
|
if (current.name === "waiting") {
|
|
1594
1957
|
cancelWaiting(false);
|
|
@@ -1610,6 +1973,7 @@ function AvatarLiveView({
|
|
|
1610
1973
|
}, [cancelStart, cancelWaiting, finish, go, reoffer]);
|
|
1611
1974
|
const leave = react.useCallback(
|
|
1612
1975
|
(event) => {
|
|
1976
|
+
micCancelRef.current();
|
|
1613
1977
|
const current = phaseRef.current;
|
|
1614
1978
|
const reason = event === "user" || event === "chat" ? "user" : "page_hide";
|
|
1615
1979
|
const how = reason === "user" ? "stopped" : "interrupted";
|
|
@@ -1620,7 +1984,7 @@ function AvatarLiveView({
|
|
|
1620
1984
|
const controller = checkRef.current;
|
|
1621
1985
|
checkRef.current = null;
|
|
1622
1986
|
controller?.abort();
|
|
1623
|
-
go(
|
|
1987
|
+
go(IDLE2);
|
|
1624
1988
|
return;
|
|
1625
1989
|
}
|
|
1626
1990
|
case "waiting":
|
|
@@ -1628,7 +1992,7 @@ function AvatarLiveView({
|
|
|
1628
1992
|
return;
|
|
1629
1993
|
case "bot-check":
|
|
1630
1994
|
cancelStart(reason);
|
|
1631
|
-
go(reason === "user" ?
|
|
1995
|
+
go(reason === "user" ? IDLE2 : reoffer(current.offer));
|
|
1632
1996
|
return;
|
|
1633
1997
|
case "creating":
|
|
1634
1998
|
if (keepsSession) return;
|
|
@@ -1690,7 +2054,7 @@ function AvatarLiveView({
|
|
|
1690
2054
|
} else {
|
|
1691
2055
|
checkRef.current?.abort();
|
|
1692
2056
|
checkRef.current = null;
|
|
1693
|
-
go(
|
|
2057
|
+
go(IDLE2);
|
|
1694
2058
|
}
|
|
1695
2059
|
}, [active, cancelWaiting, check, go, locale]);
|
|
1696
2060
|
const kind = shown.kind;
|
|
@@ -1935,6 +2299,46 @@ function AvatarLiveView({
|
|
|
1935
2299
|
const textMode = any && (usedToday && (phase.name === "needs-answer" || phase.name === "consent" || phase.name === "declined" || phase.name === "ended") || phase.name === "refused" && phase.reason === "limits");
|
|
1936
2300
|
textBoxRef.current = textMode;
|
|
1937
2301
|
const askState = !any ? void 0 : textMode ? "text" : phase.name === "needs-answer" ? kind === "empty" ? "ready" : kind : phase.name;
|
|
2302
|
+
const askReason = inFlight(conversation) ? S.askBusy : conversation?.canSend ? null : S.askUnavailable;
|
|
2303
|
+
const refreshMicOffer = react.useCallback(async () => {
|
|
2304
|
+
const { live: live2, locale: locale2, base: base2, headers: headers2 } = latest.current;
|
|
2305
|
+
if (base2 === null) return null;
|
|
2306
|
+
const next = micOfferOf(
|
|
2307
|
+
await requestAvailabilityAnswer({
|
|
2308
|
+
base: base2,
|
|
2309
|
+
headers: headers2,
|
|
2310
|
+
site: live2.site,
|
|
2311
|
+
locale: locale2
|
|
2312
|
+
})
|
|
2313
|
+
);
|
|
2314
|
+
if (next !== null) setMicOffer(next);
|
|
2315
|
+
return next;
|
|
2316
|
+
}, []);
|
|
2317
|
+
const onMicText = react.useCallback(
|
|
2318
|
+
(text) => {
|
|
2319
|
+
setDraft((current) => withHeardText(current, text, askMaxLength));
|
|
2320
|
+
if (touchFirst) return;
|
|
2321
|
+
rootRef.current?.querySelector(
|
|
2322
|
+
'[data-boff-explore="avatar-live-ask-input"]'
|
|
2323
|
+
)?.focus();
|
|
2324
|
+
},
|
|
2325
|
+
[askMaxLength, touchFirst]
|
|
2326
|
+
);
|
|
2327
|
+
const mic = useAvatarMic({
|
|
2328
|
+
enabled: live.mic === true && micOffer !== null && !off && acceptsQuestion({
|
|
2329
|
+
any,
|
|
2330
|
+
phase: phase.name,
|
|
2331
|
+
kind,
|
|
2332
|
+
textMode,
|
|
2333
|
+
reason: askReason
|
|
2334
|
+
}),
|
|
2335
|
+
locale,
|
|
2336
|
+
base,
|
|
2337
|
+
offer: micOffer,
|
|
2338
|
+
refreshOffer: refreshMicOffer,
|
|
2339
|
+
onText: onMicText
|
|
2340
|
+
});
|
|
2341
|
+
micCancelRef.current = mic.cancel;
|
|
1938
2342
|
const notNow = react.useCallback(() => {
|
|
1939
2343
|
const current = phaseRef.current;
|
|
1940
2344
|
if ((current.name === "consent" || current.name === "needs-answer") && current.offer.questions === "any") {
|
|
@@ -2097,13 +2501,13 @@ function AvatarLiveView({
|
|
|
2097
2501
|
className: "w-full max-w-sm rounded-lg border border-border p-4 text-start",
|
|
2098
2502
|
children: [
|
|
2099
2503
|
/* @__PURE__ */ jsxRuntime.jsx("h2", { id: consentTitleId, className: "text-base font-semibold", children: copy.title }),
|
|
2100
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm", children: productName === null || copy.introProduct === null ? copy.intro :
|
|
2504
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm", children: productName === null || copy.introProduct === null ? copy.intro : chunkN7E6Z2GF_js.fill(copy.introProduct, { product: productName }) }),
|
|
2101
2505
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: copy.processing }),
|
|
2102
2506
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: copy.storage }),
|
|
2103
2507
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2104
2508
|
"a",
|
|
2105
2509
|
{
|
|
2106
|
-
href:
|
|
2510
|
+
href: PRIVACY_HREF2,
|
|
2107
2511
|
target: "_blank",
|
|
2108
2512
|
rel: "noopener noreferrer",
|
|
2109
2513
|
"data-boff-explore": "avatar-live-privacy",
|
|
@@ -2195,7 +2599,7 @@ function AvatarLiveView({
|
|
|
2195
2599
|
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm tabular-nums text-muted-foreground", children: [
|
|
2196
2600
|
S.timeLeft,
|
|
2197
2601
|
" ",
|
|
2198
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { role: "timer", "data-boff-explore": "avatar-live-countdown", children:
|
|
2602
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { role: "timer", "data-boff-explore": "avatar-live-countdown", children: clock2(Math.max(0, (session.endsAt - now) / 1e3)) })
|
|
2199
2603
|
] })
|
|
2200
2604
|
] });
|
|
2201
2605
|
const liveActions = (back) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2", children: [
|
|
@@ -2277,14 +2681,21 @@ function AvatarLiveView({
|
|
|
2277
2681
|
children: lastQuestion.content
|
|
2278
2682
|
}
|
|
2279
2683
|
),
|
|
2280
|
-
lastAnswer === void 0 ? unanswered === "waiting" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: activity ?? S.gettingAnswer }) : unanswered === "failed" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: S.answerFailed }) : null : lastAnswer.status === "PENDING" || lastAnswer.status === "RUNNING" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: activity ?? S.gettingAnswer }) : lastAnswer.status === "FAILED" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: S.answerFailed }) :
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2684
|
+
lastAnswer === void 0 ? unanswered === "waiting" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: activity ?? S.gettingAnswer }) : unanswered === "failed" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: S.answerFailed }) : null : lastAnswer.status === "PENDING" || lastAnswer.status === "RUNNING" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: activity ?? S.gettingAnswer }) : lastAnswer.status === "FAILED" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: S.answerFailed }) : (
|
|
2685
|
+
// The answer's words, not its markdown: `content` is written for the chat's renderer, and shown as it is
|
|
2686
|
+
// here a link reads `[label](https://…)`. In its spoken form, as the chat's read-aloud says it: a link is its
|
|
2687
|
+
// label (the address is in Chat, one tap away), a table its rows, and the markers of emphasis, headings,
|
|
2688
|
+
// quotes and list items go (`answerDisplayText`). The words are the ones the chat shows, citation markers
|
|
2689
|
+
// included, which the sources in Chat answer to.
|
|
2690
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2691
|
+
"p",
|
|
2692
|
+
{
|
|
2693
|
+
"data-boff-explore": "avatar-live-text-answer",
|
|
2694
|
+
dir: "auto",
|
|
2695
|
+
className: "line-clamp-6 w-full max-w-sm whitespace-pre-wrap break-words text-start text-sm",
|
|
2696
|
+
children: chunkN7E6Z2GF_js.answerDisplayText(lastAnswer.content)
|
|
2697
|
+
}
|
|
2698
|
+
)
|
|
2288
2699
|
)
|
|
2289
2700
|
] });
|
|
2290
2701
|
const ended = phase.name === "ended";
|
|
@@ -2405,7 +2816,7 @@ function AvatarLiveView({
|
|
|
2405
2816
|
break;
|
|
2406
2817
|
}
|
|
2407
2818
|
}
|
|
2408
|
-
const reason =
|
|
2819
|
+
const reason = askReason;
|
|
2409
2820
|
const chips = box !== null && shown.kind === "empty" && prompts.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
2410
2821
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-medium text-muted-foreground", children: tryAsking }),
|
|
2411
2822
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex max-h-28 flex-wrap gap-2 overflow-y-auto", children: prompts.map((prompt) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2452,6 +2863,7 @@ function AvatarLiveView({
|
|
|
2452
2863
|
children: S.usedToday
|
|
2453
2864
|
}
|
|
2454
2865
|
) : null,
|
|
2866
|
+
box === "spoken" ? mic.notice : null,
|
|
2455
2867
|
box === null ? null : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2456
2868
|
AskBox,
|
|
2457
2869
|
{
|
|
@@ -2466,7 +2878,9 @@ function AvatarLiveView({
|
|
|
2466
2878
|
footer: S.askFooter,
|
|
2467
2879
|
noFiles: S.askNoFiles,
|
|
2468
2880
|
hint: box === "spoken" ? S.needsAnswerAny : null,
|
|
2469
|
-
touchFirst
|
|
2881
|
+
touchFirst,
|
|
2882
|
+
mic: box === "spoken" ? mic.button : null,
|
|
2883
|
+
micLines: box === "spoken" ? mic.lines : null
|
|
2470
2884
|
}
|
|
2471
2885
|
)
|
|
2472
2886
|
]
|
|
@@ -2613,7 +3027,9 @@ function AskBox({
|
|
|
2613
3027
|
footer,
|
|
2614
3028
|
noFiles,
|
|
2615
3029
|
hint,
|
|
2616
|
-
touchFirst
|
|
3030
|
+
touchFirst,
|
|
3031
|
+
mic,
|
|
3032
|
+
micLines
|
|
2617
3033
|
}) {
|
|
2618
3034
|
const uid = react.useId();
|
|
2619
3035
|
const reasonId = `${uid}-reason`;
|
|
@@ -2678,6 +3094,7 @@ function AskBox({
|
|
|
2678
3094
|
onDrop
|
|
2679
3095
|
}
|
|
2680
3096
|
),
|
|
3097
|
+
mic,
|
|
2681
3098
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2682
3099
|
"button",
|
|
2683
3100
|
{
|
|
@@ -2691,6 +3108,7 @@ function AskBox({
|
|
|
2691
3108
|
}
|
|
2692
3109
|
)
|
|
2693
3110
|
] }),
|
|
3111
|
+
micLines,
|
|
2694
3112
|
reason === null ? null : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2695
3113
|
"p",
|
|
2696
3114
|
{
|