@burdenoff/website-sdk 2026.1028.1 → 2026.1031.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 +159 -1
- package/dist/avatar-live/index.d.mts +11 -0
- package/dist/avatar-live/index.d.ts +11 -0
- package/dist/avatar-live/index.js +120 -0
- package/dist/avatar-live/index.js.map +1 -0
- package/dist/avatar-live/index.mjs +118 -0
- package/dist/avatar-live/index.mjs.map +1 -0
- package/dist/avatar-live-view/index.d.mts +42 -0
- package/dist/avatar-live-view/index.d.ts +42 -0
- package/dist/avatar-live-view/index.js +1422 -0
- package/dist/avatar-live-view/index.js.map +1 -0
- package/dist/avatar-live-view/index.mjs +1420 -0
- package/dist/avatar-live-view/index.mjs.map +1 -0
- package/dist/components/contact.js +9 -3
- package/dist/components/contact.js.map +1 -1
- package/dist/components/contact.mjs +9 -3
- package/dist/components/contact.mjs.map +1 -1
- package/dist/components/explore.d.mts +2 -2
- package/dist/components/explore.d.ts +2 -2
- package/dist/components/explore.js +11 -4
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +11 -4
- package/dist/components/explore.mjs.map +1 -1
- package/dist/components/partners.js +9 -3
- package/dist/components/partners.js.map +1 -1
- package/dist/components/partners.mjs +9 -3
- package/dist/components/partners.mjs.map +1 -1
- package/dist/{explore-BEgq1_0O.d.mts → explore-DUTNYUOm.d.mts} +1 -1
- package/dist/{explore-CHiP9u-4.d.ts → explore-DjQE7Xia.d.ts} +1 -1
- package/dist/explore-avatar-live-config-B171o1Yo.d.ts +283 -0
- package/dist/explore-avatar-live-config-BGsGwbcq.d.mts +283 -0
- package/dist/{explore-types-_9UJu6EA.d.mts → explore-types-Bb2Ih3d8.d.mts} +1 -1
- package/dist/{explore-types-_9UJu6EA.d.ts → explore-types-Bb2Ih3d8.d.ts} +1 -1
- package/dist/hooks/index.d.mts +3 -2
- package/dist/hooks/index.d.ts +3 -2
- package/dist/hooks/index.js +6 -1
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +6 -1
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.d.mts +16 -253
- package/dist/index.d.ts +16 -253
- package/dist/index.js +225 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +225 -41
- package/dist/index.mjs.map +1 -1
- package/dist/types-CndbmBK-.d.mts +68 -0
- package/dist/types-CndbmBK-.d.ts +68 -0
- package/dist/{use-progressive-reveal-C0hkdu_i.d.ts → use-explore-chat-DunjG-3p.d.ts} +10 -36
- package/dist/{use-progressive-reveal-BFPWbEXy.d.mts → use-explore-chat-zRuoAin6.d.mts} +10 -36
- package/dist/use-progressive-reveal-ppicEgzZ.d.mts +35 -0
- package/dist/use-progressive-reveal-ppicEgzZ.d.ts +35 -0
- package/package.json +15 -3
|
@@ -0,0 +1,1420 @@
|
|
|
1
|
+
import { useId, useState, useRef, useCallback, useEffect } from 'react';
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import { twMerge } from 'tailwind-merge';
|
|
4
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
// src/components/explore-avatar-live-config.ts
|
|
7
|
+
var SITE = /^[a-z][a-z0-9-]{1,31}$/;
|
|
8
|
+
var VERSION = /^[A-Za-z0-9._-]{1,32}$/;
|
|
9
|
+
function isAvatarSite(value) {
|
|
10
|
+
return SITE.test(value);
|
|
11
|
+
}
|
|
12
|
+
function isAvatarVersion(value) {
|
|
13
|
+
return VERSION.test(value);
|
|
14
|
+
}
|
|
15
|
+
var REVIEWED = /* @__PURE__ */ new Set(["en"]);
|
|
16
|
+
function isAvatarLiveLocaleReviewed(locale) {
|
|
17
|
+
return REVIEWED.has(locale);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/components/explore-avatar-api.ts
|
|
21
|
+
var AVAILABILITY_TIMEOUT_MS = 8e3;
|
|
22
|
+
var CREATE_TIMEOUT_MS = 3e4;
|
|
23
|
+
var MESSAGE_ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
24
|
+
var CONVERSATION_TOKEN = /^[A-Za-z0-9._~+/=-]{16,512}$/;
|
|
25
|
+
var LOCALE = /^[A-Za-z0-9-]{1,16}$/;
|
|
26
|
+
var SESSION_ID = /^avs_[0-9a-f]{32}$/;
|
|
27
|
+
var END_SECRET = /^[A-Za-z0-9_-]{43}$/;
|
|
28
|
+
var INERT = { kind: "inert" };
|
|
29
|
+
var FAILED = { kind: "failed" };
|
|
30
|
+
function isAvatarMessageId(value) {
|
|
31
|
+
return MESSAGE_ID.test(value);
|
|
32
|
+
}
|
|
33
|
+
function isAvatarConversationToken(value) {
|
|
34
|
+
return CONVERSATION_TOKEN.test(value);
|
|
35
|
+
}
|
|
36
|
+
function isRecord(value) {
|
|
37
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
38
|
+
}
|
|
39
|
+
var AVAILABILITY_REASONS = /* @__PURE__ */ new Map([
|
|
40
|
+
["site", "inert"],
|
|
41
|
+
["unavailable", "inert"],
|
|
42
|
+
["region", "region"],
|
|
43
|
+
["locale", "locale"],
|
|
44
|
+
["paused", "paused"],
|
|
45
|
+
["busy", "busy"],
|
|
46
|
+
["limits", "limits"]
|
|
47
|
+
]);
|
|
48
|
+
function readAvailability(status, body) {
|
|
49
|
+
if (status !== 200 || !isRecord(body)) return INERT;
|
|
50
|
+
const { available, reason } = body;
|
|
51
|
+
if (typeof available !== "boolean" || typeof reason !== "string") {
|
|
52
|
+
return INERT;
|
|
53
|
+
}
|
|
54
|
+
if (reason === "ok") {
|
|
55
|
+
const { consentVersion, maxSessionSeconds } = body;
|
|
56
|
+
if (available !== true || typeof consentVersion !== "string" || !isAvatarVersion(consentVersion) || typeof maxSessionSeconds !== "number" || !Number.isInteger(maxSessionSeconds) || maxSessionSeconds < 1) {
|
|
57
|
+
return INERT;
|
|
58
|
+
}
|
|
59
|
+
return { kind: "available", offer: { consentVersion, maxSessionSeconds } };
|
|
60
|
+
}
|
|
61
|
+
const meaning = available ? void 0 : AVAILABILITY_REASONS.get(reason);
|
|
62
|
+
return meaning === void 0 || meaning === "inert" ? INERT : { kind: "refused", reason: meaning };
|
|
63
|
+
}
|
|
64
|
+
var CREATE_REFUSALS = /* @__PURE__ */ new Map([
|
|
65
|
+
["busy", "busy"],
|
|
66
|
+
["limits", "limits"],
|
|
67
|
+
["region", "region"],
|
|
68
|
+
["locale", "locale"],
|
|
69
|
+
["paused", "paused"],
|
|
70
|
+
["not_eligible", "not_eligible"],
|
|
71
|
+
["answer_claimed", "answer_claimed"]
|
|
72
|
+
]);
|
|
73
|
+
function errorCode(body) {
|
|
74
|
+
if (!isRecord(body) || !isRecord(body.error)) return null;
|
|
75
|
+
const { code } = body.error;
|
|
76
|
+
return typeof code === "string" ? code : null;
|
|
77
|
+
}
|
|
78
|
+
function readCreated(body) {
|
|
79
|
+
if (!isRecord(body)) return FAILED;
|
|
80
|
+
const { sessionId, endSecret } = body;
|
|
81
|
+
if (typeof sessionId !== "string" || !SESSION_ID.test(sessionId) || typeof endSecret !== "string" || !END_SECRET.test(endSecret)) {
|
|
82
|
+
return FAILED;
|
|
83
|
+
}
|
|
84
|
+
const unusable = { kind: "unusable", sessionId, endSecret };
|
|
85
|
+
const { state, join, captions } = body;
|
|
86
|
+
const endsAt = typeof body.endsAt === "string" ? Date.parse(body.endsAt) : NaN;
|
|
87
|
+
if (state !== "pending" || !Number.isFinite(endsAt) || !isRecord(join) || !isRecord(captions)) {
|
|
88
|
+
return unusable;
|
|
89
|
+
}
|
|
90
|
+
const { livekitUrl, participantToken, workerIdentity } = join;
|
|
91
|
+
const { locale, text } = captions;
|
|
92
|
+
if (typeof livekitUrl !== "string" || !livekitUrl.startsWith("wss://") || typeof participantToken !== "string" || participantToken === "" || typeof workerIdentity !== "string" || workerIdentity === "" || typeof locale !== "string" || typeof text !== "string") {
|
|
93
|
+
return unusable;
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
kind: "created",
|
|
97
|
+
session: {
|
|
98
|
+
sessionId,
|
|
99
|
+
endSecret,
|
|
100
|
+
endsAt,
|
|
101
|
+
join: { livekitUrl, participantToken, workerIdentity },
|
|
102
|
+
captions: { locale, text }
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function readCreate(status, body) {
|
|
107
|
+
if (status === 201) return readCreated(body);
|
|
108
|
+
const code = errorCode(body);
|
|
109
|
+
if (code !== null) {
|
|
110
|
+
const refusal = CREATE_REFUSALS.get(code);
|
|
111
|
+
if (refusal !== void 0) return { kind: "refused", reason: refusal };
|
|
112
|
+
if (code === "consent") return { kind: "consent" };
|
|
113
|
+
}
|
|
114
|
+
if (status === 401 || status === 403 || status === 404 || status === 405) {
|
|
115
|
+
return INERT;
|
|
116
|
+
}
|
|
117
|
+
return FAILED;
|
|
118
|
+
}
|
|
119
|
+
async function readJson(response) {
|
|
120
|
+
try {
|
|
121
|
+
return await response.json();
|
|
122
|
+
} catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function deadline(timeoutMs, outer) {
|
|
127
|
+
const controller = new AbortController();
|
|
128
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
129
|
+
const onOuter = () => controller.abort();
|
|
130
|
+
if (outer?.aborted) controller.abort();
|
|
131
|
+
else outer?.addEventListener("abort", onOuter, { once: true });
|
|
132
|
+
return {
|
|
133
|
+
signal: controller.signal,
|
|
134
|
+
done: () => {
|
|
135
|
+
clearTimeout(timer);
|
|
136
|
+
outer?.removeEventListener("abort", onOuter);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
async function requestAvailability(target) {
|
|
141
|
+
if (!isAvatarSite(target.site) || !LOCALE.test(target.locale)) return INERT;
|
|
142
|
+
const query = new URLSearchParams({
|
|
143
|
+
site: target.site,
|
|
144
|
+
locale: target.locale
|
|
145
|
+
});
|
|
146
|
+
const timer = deadline(AVAILABILITY_TIMEOUT_MS, target.signal);
|
|
147
|
+
try {
|
|
148
|
+
const response = await fetch(`${target.base}/availability?${query}`, {
|
|
149
|
+
method: "GET",
|
|
150
|
+
credentials: "omit",
|
|
151
|
+
cache: "no-store",
|
|
152
|
+
headers: { ...target.headers },
|
|
153
|
+
signal: timer.signal
|
|
154
|
+
});
|
|
155
|
+
return readAvailability(response.status, await readJson(response));
|
|
156
|
+
} catch {
|
|
157
|
+
return INERT;
|
|
158
|
+
} finally {
|
|
159
|
+
timer.done();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async function requestCreate(target) {
|
|
163
|
+
const { body } = target;
|
|
164
|
+
const payload = JSON.stringify({
|
|
165
|
+
site: body.site,
|
|
166
|
+
locale: body.locale,
|
|
167
|
+
conversationToken: body.conversationToken,
|
|
168
|
+
messageId: body.messageId,
|
|
169
|
+
consentVersion: body.consentVersion,
|
|
170
|
+
manifestVersion: body.manifestVersion
|
|
171
|
+
});
|
|
172
|
+
const timer = deadline(CREATE_TIMEOUT_MS);
|
|
173
|
+
try {
|
|
174
|
+
const response = await fetch(`${target.base}/sessions`, {
|
|
175
|
+
method: "POST",
|
|
176
|
+
credentials: "omit",
|
|
177
|
+
cache: "no-store",
|
|
178
|
+
headers: {
|
|
179
|
+
...target.headers,
|
|
180
|
+
"Content-Type": "application/json",
|
|
181
|
+
"Idempotency-Key": target.idempotencyKey,
|
|
182
|
+
"x-web-avatar-bot-token": target.botToken
|
|
183
|
+
},
|
|
184
|
+
body: payload,
|
|
185
|
+
signal: timer.signal
|
|
186
|
+
});
|
|
187
|
+
return readCreate(response.status, await readJson(response));
|
|
188
|
+
} catch {
|
|
189
|
+
return INERT;
|
|
190
|
+
} finally {
|
|
191
|
+
timer.done();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function sendEnd(target) {
|
|
195
|
+
try {
|
|
196
|
+
void fetch(
|
|
197
|
+
`${target.base}/sessions/${encodeURIComponent(target.sessionId)}/end`,
|
|
198
|
+
{
|
|
199
|
+
method: "POST",
|
|
200
|
+
keepalive: true,
|
|
201
|
+
credentials: "omit",
|
|
202
|
+
cache: "no-store",
|
|
203
|
+
headers: { ...target.headers },
|
|
204
|
+
body: JSON.stringify({ secret: target.secret, reason: target.reason })
|
|
205
|
+
}
|
|
206
|
+
).catch(() => void 0);
|
|
207
|
+
} catch {
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
var BOT_BINDING_PREFIX = "boff-web-avatar-cdata/v1";
|
|
211
|
+
function webCryptoSubtle() {
|
|
212
|
+
const subtle = globalThis.crypto?.subtle;
|
|
213
|
+
return typeof subtle?.digest === "function" ? subtle : null;
|
|
214
|
+
}
|
|
215
|
+
function canBindBotCheck() {
|
|
216
|
+
return webCryptoSubtle() !== null;
|
|
217
|
+
}
|
|
218
|
+
function base64UrlNoPad(bytes) {
|
|
219
|
+
let binary = "";
|
|
220
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
221
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
222
|
+
}
|
|
223
|
+
async function avatarBotBinding(values) {
|
|
224
|
+
const subtle = webCryptoSubtle();
|
|
225
|
+
if (subtle === null) {
|
|
226
|
+
throw new Error("The bot check cannot be bound here: no WebCrypto.");
|
|
227
|
+
}
|
|
228
|
+
const text = [
|
|
229
|
+
BOT_BINDING_PREFIX,
|
|
230
|
+
values.site,
|
|
231
|
+
values.locale,
|
|
232
|
+
values.conversationToken,
|
|
233
|
+
values.messageId
|
|
234
|
+
].join("\n");
|
|
235
|
+
const digest = await subtle.digest("SHA-256", new TextEncoder().encode(text));
|
|
236
|
+
return base64UrlNoPad(new Uint8Array(digest));
|
|
237
|
+
}
|
|
238
|
+
function newIdempotencyKey() {
|
|
239
|
+
const crypto = globalThis.crypto;
|
|
240
|
+
if (typeof crypto?.randomUUID === "function") return crypto.randomUUID();
|
|
241
|
+
if (typeof crypto?.getRandomValues !== "function") return null;
|
|
242
|
+
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
243
|
+
bytes[6] = (bytes[6] ?? 0) & 15 | 64;
|
|
244
|
+
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
245
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join(
|
|
246
|
+
""
|
|
247
|
+
);
|
|
248
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// src/components/explore-avatar-i18n.ts
|
|
252
|
+
var EN = {
|
|
253
|
+
checking: "Checking whether the live avatar is available\u2026",
|
|
254
|
+
needsAnswer: "Choose a suggested question in Chat first.",
|
|
255
|
+
needsAnswerDetail: "The avatar can read only the first answer aloud.",
|
|
256
|
+
startNewChat: "Start a new chat to use the avatar.",
|
|
257
|
+
startNewChatDetail: "The avatar reads aloud only the first answer in a chat, and only to a suggested question. Start a new chat and choose a suggested question.",
|
|
258
|
+
start: "Start live avatar",
|
|
259
|
+
notNow: "Not now",
|
|
260
|
+
opensInNewTab: "(opens in a new tab)",
|
|
261
|
+
botCheck: "Checking your browser\u2026",
|
|
262
|
+
connecting: "Connecting to the live avatar\u2026",
|
|
263
|
+
speaking: "The avatar is speaking.",
|
|
264
|
+
badge: "AI avatar \xB7 not a real person",
|
|
265
|
+
video: "Live AI avatar video",
|
|
266
|
+
captions: "Captions",
|
|
267
|
+
timeLeft: "Time left",
|
|
268
|
+
stop: "Stop",
|
|
269
|
+
tapForSound: "Tap to turn on sound",
|
|
270
|
+
finished: "The avatar has finished.",
|
|
271
|
+
stopped: "You stopped the avatar.",
|
|
272
|
+
failed: "The live avatar couldn't start.",
|
|
273
|
+
interrupted: "The live avatar stopped early.",
|
|
274
|
+
answerInChat: "The full answer is in Chat.",
|
|
275
|
+
backToChat: "Back to Chat",
|
|
276
|
+
busy: "The live avatar is busy right now. Please try again in a few minutes.",
|
|
277
|
+
limits: "The live avatar has reached its limit for now. Please try again later.",
|
|
278
|
+
region: "The live avatar isn't offered in your region yet.",
|
|
279
|
+
locale: "The live avatar isn't offered in this language yet.",
|
|
280
|
+
paused: "The live avatar is paused right now.",
|
|
281
|
+
notEligible: "The avatar can read answers to the suggested questions.",
|
|
282
|
+
answerClaimed: "The avatar has already read this answer."
|
|
283
|
+
};
|
|
284
|
+
var ZH = {
|
|
285
|
+
checking: "\u6B63\u5728\u68C0\u67E5\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u662F\u5426\u53EF\u7528\u2026",
|
|
286
|
+
needsAnswer: "\u8BF7\u5148\u5728\u804A\u5929\u4E2D\u9009\u62E9\u4E00\u4E2A\u63A8\u8350\u95EE\u9898\u3002",
|
|
287
|
+
needsAnswerDetail: "\u865A\u62DF\u5F62\u8C61\u53EA\u80FD\u6717\u8BFB\u7B2C\u4E00\u4E2A\u56DE\u7B54\u3002",
|
|
288
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft.
|
|
289
|
+
startNewChat: "\u8BF7\u5F00\u59CB\u65B0\u7684\u804A\u5929\uFF0C\u518D\u4F7F\u7528\u865A\u62DF\u5F62\u8C61\u3002",
|
|
290
|
+
startNewChatDetail: "\u865A\u62DF\u5F62\u8C61\u53EA\u6717\u8BFB\u4E00\u6B21\u804A\u5929\u4E2D\u7684\u7B2C\u4E00\u4E2A\u56DE\u7B54\uFF0C\u4E14\u4EC5\u9650\u56DE\u7B54\u63A8\u8350\u95EE\u9898\u3002\u8BF7\u5F00\u59CB\u65B0\u7684\u804A\u5929\uFF0C\u5E76\u9009\u62E9\u4E00\u4E2A\u63A8\u8350\u95EE\u9898\u3002",
|
|
291
|
+
start: "\u542F\u52A8\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61",
|
|
292
|
+
notNow: "\u6682\u4E0D",
|
|
293
|
+
opensInNewTab: "\uFF08\u5728\u65B0\u6807\u7B7E\u9875\u4E2D\u6253\u5F00\uFF09",
|
|
294
|
+
botCheck: "\u6B63\u5728\u68C0\u67E5\u60A8\u7684\u6D4F\u89C8\u5668\u2026",
|
|
295
|
+
connecting: "\u6B63\u5728\u8FDE\u63A5\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u2026",
|
|
296
|
+
speaking: "\u865A\u62DF\u5F62\u8C61\u6B63\u5728\u8BB2\u8BDD\u3002",
|
|
297
|
+
badge: "AI \u865A\u62DF\u5F62\u8C61 \xB7 \u4E0D\u662F\u771F\u4EBA",
|
|
298
|
+
video: "\u5B9E\u65F6 AI \u865A\u62DF\u5F62\u8C61\u89C6\u9891",
|
|
299
|
+
captions: "\u5B57\u5E55",
|
|
300
|
+
timeLeft: "\u5269\u4F59\u65F6\u95F4",
|
|
301
|
+
stop: "\u505C\u6B62",
|
|
302
|
+
tapForSound: "\u70B9\u6309\u4EE5\u5F00\u542F\u58F0\u97F3",
|
|
303
|
+
finished: "\u865A\u62DF\u5F62\u8C61\u5DF2\u8BB2\u5B8C\u3002",
|
|
304
|
+
stopped: "\u60A8\u5DF2\u505C\u6B62\u865A\u62DF\u5F62\u8C61\u3002",
|
|
305
|
+
failed: "\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u65E0\u6CD5\u542F\u52A8\u3002",
|
|
306
|
+
interrupted: "\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u63D0\u524D\u505C\u6B62\u4E86\u3002",
|
|
307
|
+
answerInChat: "\u5B8C\u6574\u56DE\u7B54\u5728\u804A\u5929\u4E2D\u3002",
|
|
308
|
+
backToChat: "\u8FD4\u56DE\u804A\u5929",
|
|
309
|
+
busy: "\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u76EE\u524D\u7E41\u5FD9\uFF0C\u8BF7\u51E0\u5206\u949F\u540E\u518D\u8BD5\u3002",
|
|
310
|
+
limits: "\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u6682\u65F6\u5DF2\u8FBE\u5230\u4F7F\u7528\u4E0A\u9650\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002",
|
|
311
|
+
region: "\u60A8\u6240\u5728\u7684\u5730\u533A\u6682\u4E0D\u63D0\u4F9B\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u3002",
|
|
312
|
+
locale: "\u6B64\u8BED\u8A00\u6682\u4E0D\u63D0\u4F9B\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u3002",
|
|
313
|
+
paused: "\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u76EE\u524D\u5DF2\u6682\u505C\u3002",
|
|
314
|
+
notEligible: "\u865A\u62DF\u5F62\u8C61\u53EF\u4EE5\u6717\u8BFB\u63A8\u8350\u95EE\u9898\u7684\u56DE\u7B54\u3002",
|
|
315
|
+
answerClaimed: "\u865A\u62DF\u5F62\u8C61\u5DF2\u7ECF\u6717\u8BFB\u8FC7\u8FD9\u4E2A\u56DE\u7B54\u3002"
|
|
316
|
+
};
|
|
317
|
+
var HI = {
|
|
318
|
+
checking: "\u091C\u093E\u0901\u091A \u0939\u094B \u0930\u0939\u0940 \u0939\u0948 \u0915\u093F \u0932\u093E\u0907\u0935 \u0905\u0935\u0924\u093E\u0930 \u0909\u092A\u0932\u092C\u094D\u0927 \u0939\u0948 \u092F\u093E \u0928\u0939\u0940\u0902\u2026",
|
|
319
|
+
needsAnswer: "\u092A\u0939\u0932\u0947 \u091A\u0948\u091F \u092E\u0947\u0902 \u0915\u094B\u0908 \u0938\u0941\u091D\u093E\u092F\u093E \u0917\u092F\u093E \u092A\u094D\u0930\u0936\u094D\u0928 \u091A\u0941\u0928\u0947\u0902\u0964",
|
|
320
|
+
needsAnswerDetail: "\u0905\u0935\u0924\u093E\u0930 \u0915\u0947\u0935\u0932 \u092A\u0939\u0932\u093E \u0909\u0924\u094D\u0924\u0930 \u091C\u093C\u094B\u0930 \u0938\u0947 \u092A\u0922\u093C \u0938\u0915\u0924\u093E \u0939\u0948\u0964",
|
|
321
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft.
|
|
322
|
+
startNewChat: "\u0905\u0935\u0924\u093E\u0930 \u0915\u093E \u0909\u092A\u092F\u094B\u0917 \u0915\u0930\u0928\u0947 \u0915\u0947 \u0932\u093F\u090F \u0928\u0908 \u091A\u0948\u091F \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902\u0964",
|
|
323
|
+
startNewChatDetail: "\u0905\u0935\u0924\u093E\u0930 \u0915\u093F\u0938\u0940 \u091A\u0948\u091F \u0915\u093E \u0915\u0947\u0935\u0932 \u092A\u0939\u0932\u093E \u0909\u0924\u094D\u0924\u0930 \u091C\u093C\u094B\u0930 \u0938\u0947 \u092A\u0922\u093C\u0924\u093E \u0939\u0948, \u0914\u0930 \u0935\u0939 \u092D\u0940 \u0938\u0941\u091D\u093E\u090F \u0917\u090F \u092A\u094D\u0930\u0936\u094D\u0928 \u0915\u093E \u0939\u0940\u0964 \u0928\u0908 \u091A\u0948\u091F \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902 \u0914\u0930 \u0915\u094B\u0908 \u0938\u0941\u091D\u093E\u092F\u093E \u0917\u092F\u093E \u092A\u094D\u0930\u0936\u094D\u0928 \u091A\u0941\u0928\u0947\u0902\u0964",
|
|
324
|
+
start: "\u0932\u093E\u0907\u0935 \u0905\u0935\u0924\u093E\u0930 \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902",
|
|
325
|
+
notNow: "\u0905\u092D\u0940 \u0928\u0939\u0940\u0902",
|
|
326
|
+
opensInNewTab: "(\u0928\u090F \u091F\u0948\u092C \u092E\u0947\u0902 \u0916\u0941\u0932\u0924\u093E \u0939\u0948)",
|
|
327
|
+
botCheck: "\u0906\u092A\u0915\u0947 \u092C\u094D\u0930\u093E\u0909\u091C\u093C\u0930 \u0915\u0940 \u091C\u093E\u0901\u091A \u0939\u094B \u0930\u0939\u0940 \u0939\u0948\u2026",
|
|
328
|
+
connecting: "\u0932\u093E\u0907\u0935 \u0905\u0935\u0924\u093E\u0930 \u0938\u0947 \u091C\u0941\u0921\u093C \u0930\u0939\u0947 \u0939\u0948\u0902\u2026",
|
|
329
|
+
speaking: "\u0905\u0935\u0924\u093E\u0930 \u092C\u094B\u0932 \u0930\u0939\u093E \u0939\u0948\u0964",
|
|
330
|
+
badge: "AI \u0905\u0935\u0924\u093E\u0930 \xB7 \u0905\u0938\u0932\u0940 \u0935\u094D\u092F\u0915\u094D\u0924\u093F \u0928\u0939\u0940\u0902",
|
|
331
|
+
video: "\u0932\u093E\u0907\u0935 AI \u0905\u0935\u0924\u093E\u0930 \u0935\u0940\u0921\u093F\u092F\u094B",
|
|
332
|
+
captions: "\u0915\u0948\u092A\u094D\u0936\u0928",
|
|
333
|
+
timeLeft: "\u092C\u091A\u093E \u0939\u0941\u0906 \u0938\u092E\u092F",
|
|
334
|
+
stop: "\u0930\u094B\u0915\u0947\u0902",
|
|
335
|
+
tapForSound: "\u0906\u0935\u093E\u091C\u093C \u091A\u093E\u0932\u0942 \u0915\u0930\u0928\u0947 \u0915\u0947 \u0932\u093F\u090F \u091F\u0948\u092A \u0915\u0930\u0947\u0902",
|
|
336
|
+
finished: "\u0905\u0935\u0924\u093E\u0930 \u0928\u0947 \u092A\u0922\u093C\u0928\u093E \u092A\u0942\u0930\u093E \u0915\u0930 \u0932\u093F\u092F\u093E \u0939\u0948\u0964",
|
|
337
|
+
stopped: "\u0906\u092A\u0928\u0947 \u0905\u0935\u0924\u093E\u0930 \u0915\u094B \u0930\u094B\u0915 \u0926\u093F\u092F\u093E\u0964",
|
|
338
|
+
failed: "\u0932\u093E\u0907\u0935 \u0905\u0935\u0924\u093E\u0930 \u0936\u0941\u0930\u0942 \u0928\u0939\u0940\u0902 \u0939\u094B \u0938\u0915\u093E\u0964",
|
|
339
|
+
interrupted: "\u0932\u093E\u0907\u0935 \u0905\u0935\u0924\u093E\u0930 \u092C\u0940\u091A \u092E\u0947\u0902 \u0939\u0940 \u0930\u0941\u0915 \u0917\u092F\u093E\u0964",
|
|
340
|
+
answerInChat: "\u092A\u0942\u0930\u093E \u0909\u0924\u094D\u0924\u0930 \u091A\u0948\u091F \u092E\u0947\u0902 \u0939\u0948\u0964",
|
|
341
|
+
backToChat: "\u091A\u0948\u091F \u092A\u0930 \u0935\u093E\u092A\u0938 \u091C\u093E\u090F\u0901",
|
|
342
|
+
busy: "\u0932\u093E\u0907\u0935 \u0905\u0935\u0924\u093E\u0930 \u0905\u092D\u0940 \u0935\u094D\u092F\u0938\u094D\u0924 \u0939\u0948\u0964 \u0915\u0943\u092A\u092F\u093E \u0915\u0941\u091B \u092E\u093F\u0928\u091F \u092C\u093E\u0926 \u092B\u093F\u0930 \u0915\u094B\u0936\u093F\u0936 \u0915\u0930\u0947\u0902\u0964",
|
|
343
|
+
limits: "\u0932\u093E\u0907\u0935 \u0905\u0935\u0924\u093E\u0930 \u0905\u092D\u0940 \u0905\u092A\u0928\u0940 \u0938\u0940\u092E\u093E \u0924\u0915 \u092A\u0939\u0941\u0901\u091A \u0917\u092F\u093E \u0939\u0948\u0964 \u0915\u0943\u092A\u092F\u093E \u092C\u093E\u0926 \u092E\u0947\u0902 \u092B\u093F\u0930 \u0915\u094B\u0936\u093F\u0936 \u0915\u0930\u0947\u0902\u0964",
|
|
344
|
+
region: "\u0906\u092A\u0915\u0947 \u0915\u094D\u0937\u0947\u0924\u094D\u0930 \u092E\u0947\u0902 \u0932\u093E\u0907\u0935 \u0905\u0935\u0924\u093E\u0930 \u0905\u092D\u0940 \u0909\u092A\u0932\u092C\u094D\u0927 \u0928\u0939\u0940\u0902 \u0939\u0948\u0964",
|
|
345
|
+
locale: "\u0907\u0938 \u092D\u093E\u0937\u093E \u092E\u0947\u0902 \u0932\u093E\u0907\u0935 \u0905\u0935\u0924\u093E\u0930 \u0905\u092D\u0940 \u0909\u092A\u0932\u092C\u094D\u0927 \u0928\u0939\u0940\u0902 \u0939\u0948\u0964",
|
|
346
|
+
paused: "\u0932\u093E\u0907\u0935 \u0905\u0935\u0924\u093E\u0930 \u0905\u092D\u0940 \u0930\u0941\u0915\u093E \u0939\u0941\u0906 \u0939\u0948\u0964",
|
|
347
|
+
notEligible: "\u0905\u0935\u0924\u093E\u0930 \u0938\u0941\u091D\u093E\u090F \u0917\u090F \u092A\u094D\u0930\u0936\u094D\u0928\u094B\u0902 \u0915\u0947 \u0909\u0924\u094D\u0924\u0930 \u092A\u0922\u093C \u0938\u0915\u0924\u093E \u0939\u0948\u0964",
|
|
348
|
+
answerClaimed: "\u0905\u0935\u0924\u093E\u0930 \u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u092A\u0939\u0932\u0947 \u0939\u0940 \u092A\u0922\u093C \u091A\u0941\u0915\u093E \u0939\u0948\u0964"
|
|
349
|
+
};
|
|
350
|
+
var ES = {
|
|
351
|
+
checking: "Comprobando si el avatar en vivo est\xE1 disponible\u2026",
|
|
352
|
+
needsAnswer: "Primero elige una pregunta sugerida en el chat.",
|
|
353
|
+
needsAnswerDetail: "El avatar solo puede leer en voz alta la primera respuesta.",
|
|
354
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft.
|
|
355
|
+
startNewChat: "Inicia un chat nuevo para usar el avatar.",
|
|
356
|
+
startNewChatDetail: "El avatar solo lee en voz alta la primera respuesta de un chat, y solo si es la de una pregunta sugerida. Inicia un chat nuevo y elige una pregunta sugerida.",
|
|
357
|
+
start: "Iniciar el avatar en vivo",
|
|
358
|
+
notNow: "Ahora no",
|
|
359
|
+
opensInNewTab: "(se abre en una pesta\xF1a nueva)",
|
|
360
|
+
botCheck: "Comprobando tu navegador\u2026",
|
|
361
|
+
connecting: "Conectando con el avatar en vivo\u2026",
|
|
362
|
+
speaking: "El avatar est\xE1 hablando.",
|
|
363
|
+
badge: "Avatar de IA \xB7 no es una persona real",
|
|
364
|
+
video: "V\xEDdeo del avatar de IA en vivo",
|
|
365
|
+
captions: "Subt\xEDtulos",
|
|
366
|
+
timeLeft: "Tiempo restante",
|
|
367
|
+
stop: "Detener",
|
|
368
|
+
tapForSound: "Toca para activar el sonido",
|
|
369
|
+
finished: "El avatar ha terminado.",
|
|
370
|
+
stopped: "Has detenido el avatar.",
|
|
371
|
+
failed: "No se pudo iniciar el avatar en vivo.",
|
|
372
|
+
interrupted: "El avatar en vivo se detuvo antes de tiempo.",
|
|
373
|
+
answerInChat: "La respuesta completa est\xE1 en el chat.",
|
|
374
|
+
backToChat: "Volver al chat",
|
|
375
|
+
busy: "El avatar en vivo est\xE1 ocupado en este momento. Vuelve a intentarlo en unos minutos.",
|
|
376
|
+
limits: "El avatar en vivo ha alcanzado su l\xEDmite por ahora. Vuelve a intentarlo m\xE1s tarde.",
|
|
377
|
+
region: "El avatar en vivo a\xFAn no est\xE1 disponible en tu regi\xF3n.",
|
|
378
|
+
locale: "El avatar en vivo a\xFAn no est\xE1 disponible en este idioma.",
|
|
379
|
+
paused: "El avatar en vivo est\xE1 en pausa en este momento.",
|
|
380
|
+
notEligible: "El avatar puede leer las respuestas a las preguntas sugeridas.",
|
|
381
|
+
answerClaimed: "El avatar ya ha le\xEDdo esta respuesta."
|
|
382
|
+
};
|
|
383
|
+
var AR = {
|
|
384
|
+
checking: "\u062C\u0627\u0631\u064D \u0627\u0644\u062A\u062D\u0642\u0642 \u0645\u0646 \u062A\u0648\u0641\u0631 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0645\u0628\u0627\u0634\u0631\u2026",
|
|
385
|
+
needsAnswer: "\u0627\u062E\u062A\u0631 \u0623\u0648\u0644\u064B\u0627 \u0633\u0624\u0627\u0644\u064B\u0627 \u0645\u0642\u062A\u0631\u062D\u064B\u0627 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629.",
|
|
386
|
+
needsAnswerDetail: "\u064A\u0645\u0643\u0646 \u0644\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0642\u0631\u0627\u0621\u0629 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0627\u0644\u0623\u0648\u0644\u0649 \u0641\u0642\u0637 \u0628\u0635\u0648\u062A \u0639\u0627\u0644\u064D.",
|
|
387
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft.
|
|
388
|
+
startNewChat: "\u0627\u0628\u062F\u0623 \u062F\u0631\u062F\u0634\u0629 \u062C\u062F\u064A\u062F\u0629 \u0644\u0627\u0633\u062A\u062E\u062F\u0627\u0645 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631.",
|
|
389
|
+
startNewChatDetail: "\u064A\u0642\u0631\u0623 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0628\u0635\u0648\u062A \u0639\u0627\u0644\u064D \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0627\u0644\u0623\u0648\u0644\u0649 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629 \u0641\u0642\u0637\u060C \u0648\u0641\u0642\u0637 \u0625\u0630\u0627 \u0643\u0627\u0646\u062A \u0625\u062C\u0627\u0628\u0629 \u0639\u0646 \u0633\u0624\u0627\u0644 \u0645\u0642\u062A\u0631\u062D. \u0627\u0628\u062F\u0623 \u062F\u0631\u062F\u0634\u0629 \u062C\u062F\u064A\u062F\u0629 \u0648\u0627\u062E\u062A\u0631 \u0633\u0624\u0627\u0644\u064B\u0627 \u0645\u0642\u062A\u0631\u062D\u064B\u0627.",
|
|
390
|
+
start: "\u0628\u062F\u0621 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0645\u0628\u0627\u0634\u0631",
|
|
391
|
+
notNow: "\u0644\u064A\u0633 \u0627\u0644\u0622\u0646",
|
|
392
|
+
opensInNewTab: "(\u064A\u064F\u0641\u062A\u062D \u0641\u064A \u0639\u0644\u0627\u0645\u0629 \u062A\u0628\u0648\u064A\u0628 \u062C\u062F\u064A\u062F\u0629)",
|
|
393
|
+
botCheck: "\u062C\u0627\u0631\u064D \u0627\u0644\u062A\u062D\u0642\u0642 \u0645\u0646 \u0645\u062A\u0635\u0641\u062D\u0643\u2026",
|
|
394
|
+
connecting: "\u062C\u0627\u0631\u064D \u0627\u0644\u0627\u062A\u0635\u0627\u0644 \u0628\u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0645\u0628\u0627\u0634\u0631\u2026",
|
|
395
|
+
speaking: "\u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u064A\u062A\u062D\u062F\u062B \u0627\u0644\u0622\u0646.",
|
|
396
|
+
badge: "\u0623\u0641\u0627\u062A\u0627\u0631 \u0628\u0627\u0644\u0630\u0643\u0627\u0621 \u0627\u0644\u0627\u0635\u0637\u0646\u0627\u0639\u064A \xB7 \u0644\u064A\u0633 \u0634\u062E\u0635\u064B\u0627 \u062D\u0642\u064A\u0642\u064A\u064B\u0627",
|
|
397
|
+
video: "\u0641\u064A\u062F\u064A\u0648 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0645\u0628\u0627\u0634\u0631 \u0628\u0627\u0644\u0630\u0643\u0627\u0621 \u0627\u0644\u0627\u0635\u0637\u0646\u0627\u0639\u064A",
|
|
398
|
+
captions: "\u0627\u0644\u062A\u0633\u0645\u064A\u0627\u062A \u0627\u0644\u062A\u0648\u0636\u064A\u062D\u064A\u0629",
|
|
399
|
+
timeLeft: "\u0627\u0644\u0648\u0642\u062A \u0627\u0644\u0645\u062A\u0628\u0642\u064A",
|
|
400
|
+
stop: "\u0625\u064A\u0642\u0627\u0641",
|
|
401
|
+
tapForSound: "\u0627\u0646\u0642\u0631 \u0644\u062A\u0634\u063A\u064A\u0644 \u0627\u0644\u0635\u0648\u062A",
|
|
402
|
+
finished: "\u0627\u0646\u062A\u0647\u0649 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631.",
|
|
403
|
+
stopped: "\u0644\u0642\u062F \u0623\u0648\u0642\u0641\u062A \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631.",
|
|
404
|
+
failed: "\u062A\u0639\u0630\u0651\u0631 \u0628\u062F\u0621 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0645\u0628\u0627\u0634\u0631.",
|
|
405
|
+
interrupted: "\u062A\u0648\u0642\u0641 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0645\u0628\u0627\u0634\u0631 \u0642\u0628\u0644 \u0623\u0646 \u064A\u0646\u062A\u0647\u064A.",
|
|
406
|
+
answerInChat: "\u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0627\u0644\u0643\u0627\u0645\u0644\u0629 \u0645\u0648\u062C\u0648\u062F\u0629 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629.",
|
|
407
|
+
backToChat: "\u0627\u0644\u0639\u0648\u062F\u0629 \u0625\u0644\u0649 \u0627\u0644\u062F\u0631\u062F\u0634\u0629",
|
|
408
|
+
busy: "\u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0645\u0628\u0627\u0634\u0631 \u0645\u0634\u063A\u0648\u0644 \u0627\u0644\u0622\u0646. \u064A\u064F\u0631\u062C\u0649 \u0627\u0644\u0645\u062D\u0627\u0648\u0644\u0629 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649 \u0628\u0639\u062F \u0628\u0636\u0639 \u062F\u0642\u0627\u0626\u0642.",
|
|
409
|
+
limits: "\u0628\u0644\u063A \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0645\u0628\u0627\u0634\u0631 \u062D\u062F\u0651\u0647 \u0641\u064A \u0627\u0644\u0648\u0642\u062A \u0627\u0644\u062D\u0627\u0644\u064A. \u064A\u064F\u0631\u062C\u0649 \u0627\u0644\u0645\u062D\u0627\u0648\u0644\u0629 \u0644\u0627\u062D\u0642\u064B\u0627.",
|
|
410
|
+
region: "\u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0645\u0628\u0627\u0634\u0631 \u063A\u064A\u0631 \u0645\u062A\u0627\u062D \u0641\u064A \u0645\u0646\u0637\u0642\u062A\u0643 \u0628\u0639\u062F.",
|
|
411
|
+
locale: "\u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0645\u0628\u0627\u0634\u0631 \u063A\u064A\u0631 \u0645\u062A\u0627\u062D \u0628\u0647\u0630\u0647 \u0627\u0644\u0644\u063A\u0629 \u0628\u0639\u062F.",
|
|
412
|
+
paused: "\u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0645\u0628\u0627\u0634\u0631 \u0645\u062A\u0648\u0642\u0641 \u0645\u0624\u0642\u062A\u064B\u0627 \u0627\u0644\u0622\u0646.",
|
|
413
|
+
notEligible: "\u064A\u0645\u0643\u0646 \u0644\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0642\u0631\u0627\u0621\u0629 \u0625\u062C\u0627\u0628\u0627\u062A \u0627\u0644\u0623\u0633\u0626\u0644\u0629 \u0627\u0644\u0645\u0642\u062A\u0631\u062D\u0629.",
|
|
414
|
+
answerClaimed: "\u0642\u0631\u0623 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0645\u0646 \u0642\u0628\u0644."
|
|
415
|
+
};
|
|
416
|
+
var TA = {
|
|
417
|
+
checking: "\u0BA8\u0BC7\u0BB0\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0B95\u0BBF\u0B9F\u0BC8\u0B95\u0BCD\u0B95\u0BBF\u0BB1\u0BA4\u0BBE \u0B8E\u0BA9\u0B9A\u0BCD \u0B9A\u0BB0\u0BBF\u0BAA\u0BBE\u0BB0\u0BCD\u0B95\u0BCD\u0B95\u0BBF\u0BB1\u0BA4\u0BC1\u2026",
|
|
418
|
+
needsAnswer: "\u0BAE\u0BC1\u0BA4\u0BB2\u0BBF\u0BB2\u0BCD \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0BAA\u0BB0\u0BBF\u0BA8\u0BCD\u0BA4\u0BC1\u0BB0\u0BC8\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F \u0B92\u0BB0\u0BC1 \u0B95\u0BC7\u0BB3\u0BCD\u0BB5\u0BBF\u0BAF\u0BC8\u0BA4\u0BCD \u0BA4\u0BC7\u0BB0\u0BCD\u0BA8\u0BCD\u0BA4\u0BC6\u0B9F\u0BC1\u0B95\u0BCD\u0B95\u0BB5\u0BC1\u0BAE\u0BCD.",
|
|
419
|
+
needsAnswerDetail: "\u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BAE\u0BC1\u0BA4\u0BB2\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0BAE\u0B9F\u0BCD\u0B9F\u0BC1\u0BAE\u0BC7 \u0B89\u0BB0\u0B95\u0BCD\u0B95\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95 \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BC1\u0BAE\u0BCD.",
|
|
420
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft.
|
|
421
|
+
startNewChat: "\u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BC8\u0BAA\u0BCD \u0BAA\u0BAF\u0BA9\u0BCD\u0BAA\u0B9F\u0BC1\u0BA4\u0BCD\u0BA4 \u0BAA\u0BC1\u0BA4\u0BBF\u0BAF \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BC8\u0BA4\u0BCD \u0BA4\u0BCA\u0B9F\u0B99\u0BCD\u0B95\u0BB5\u0BC1\u0BAE\u0BCD.",
|
|
422
|
+
startNewChatDetail: "\u0B93\u0BB0\u0BCD \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BA9\u0BCD \u0BAE\u0BC1\u0BA4\u0BB2\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0BAE\u0B9F\u0BCD\u0B9F\u0BC1\u0BAE\u0BC7, \u0B85\u0BA4\u0BC1\u0BB5\u0BC1\u0BAE\u0BCD \u0BAA\u0BB0\u0BBF\u0BA8\u0BCD\u0BA4\u0BC1\u0BB0\u0BC8\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F \u0B95\u0BC7\u0BB3\u0BCD\u0BB5\u0BBF\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0BAE\u0B9F\u0BCD\u0B9F\u0BC1\u0BAE\u0BC7 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0B89\u0BB0\u0B95\u0BCD\u0B95\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95\u0BC1\u0BAE\u0BCD. \u0BAA\u0BC1\u0BA4\u0BBF\u0BAF \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BC8\u0BA4\u0BCD \u0BA4\u0BCA\u0B9F\u0B99\u0BCD\u0B95\u0BBF, \u0BAA\u0BB0\u0BBF\u0BA8\u0BCD\u0BA4\u0BC1\u0BB0\u0BC8\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F \u0B92\u0BB0\u0BC1 \u0B95\u0BC7\u0BB3\u0BCD\u0BB5\u0BBF\u0BAF\u0BC8\u0BA4\u0BCD \u0BA4\u0BC7\u0BB0\u0BCD\u0BA8\u0BCD\u0BA4\u0BC6\u0B9F\u0BC1\u0B95\u0BCD\u0B95\u0BB5\u0BC1\u0BAE\u0BCD.",
|
|
423
|
+
start: "\u0BA8\u0BC7\u0BB0\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BC8\u0BA4\u0BCD \u0BA4\u0BCA\u0B9F\u0B99\u0BCD\u0B95\u0BC1",
|
|
424
|
+
notNow: "\u0B87\u0BAA\u0BCD\u0BAA\u0BCB\u0BA4\u0BC1 \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BBE\u0BAE\u0BCD",
|
|
425
|
+
opensInNewTab: "(\u0BAA\u0BC1\u0BA4\u0BBF\u0BAF \u0BA4\u0BBE\u0BB5\u0BB2\u0BBF\u0BB2\u0BCD \u0BA4\u0BBF\u0BB1\u0B95\u0BCD\u0B95\u0BC1\u0BAE\u0BCD)",
|
|
426
|
+
botCheck: "\u0B89\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B89\u0BB2\u0BBE\u0BB5\u0BBF\u0BAF\u0BC8\u0B9A\u0BCD \u0B9A\u0BB0\u0BBF\u0BAA\u0BBE\u0BB0\u0BCD\u0B95\u0BCD\u0B95\u0BBF\u0BB1\u0BA4\u0BC1\u2026",
|
|
427
|
+
connecting: "\u0BA8\u0BC7\u0BB0\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BC1\u0B9F\u0BA9\u0BCD \u0B87\u0BA3\u0BC8\u0B95\u0BCD\u0B95\u0BBF\u0BB1\u0BA4\u0BC1\u2026",
|
|
428
|
+
speaking: "\u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BAA\u0BC7\u0B9A\u0BC1\u0B95\u0BBF\u0BB1\u0BA4\u0BC1.",
|
|
429
|
+
badge: "AI \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \xB7 \u0B89\u0BA3\u0BCD\u0BAE\u0BC8\u0BAF\u0BBE\u0BA9 \u0BA8\u0BAA\u0BB0\u0BCD \u0B85\u0BB2\u0BCD\u0BB2",
|
|
430
|
+
video: "\u0BA8\u0BC7\u0BB0\u0BB2\u0BC8 AI \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BB5\u0BC0\u0B9F\u0BBF\u0BAF\u0BCB",
|
|
431
|
+
captions: "\u0BB5\u0B9A\u0BA9\u0B99\u0BCD\u0B95\u0BB3\u0BCD",
|
|
432
|
+
timeLeft: "\u0BAE\u0BC0\u0BA4\u0BAE\u0BC1\u0BB3\u0BCD\u0BB3 \u0BA8\u0BC7\u0BB0\u0BAE\u0BCD",
|
|
433
|
+
stop: "\u0BA8\u0BBF\u0BB1\u0BC1\u0BA4\u0BCD\u0BA4\u0BC1",
|
|
434
|
+
tapForSound: "\u0B92\u0BB2\u0BBF\u0BAF\u0BC8 \u0B87\u0BAF\u0B95\u0BCD\u0B95\u0BA4\u0BCD \u0BA4\u0B9F\u0BCD\u0B9F\u0BB5\u0BC1\u0BAE\u0BCD",
|
|
435
|
+
finished: "\u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BAA\u0B9F\u0BBF\u0BA4\u0BCD\u0BA4\u0BC1 \u0BAE\u0BC1\u0B9F\u0BBF\u0BA4\u0BCD\u0BA4\u0BA4\u0BC1.",
|
|
436
|
+
stopped: "\u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BC8 \u0BA8\u0BBF\u0BB1\u0BC1\u0BA4\u0BCD\u0BA4\u0BBF\u0BA9\u0BC0\u0BB0\u0BCD\u0B95\u0BB3\u0BCD.",
|
|
437
|
+
failed: "\u0BA8\u0BC7\u0BB0\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BC8\u0BA4\u0BCD \u0BA4\u0BCA\u0B9F\u0B99\u0BCD\u0B95 \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8.",
|
|
438
|
+
interrupted: "\u0BA8\u0BC7\u0BB0\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0B87\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BC7\u0BAF\u0BC7 \u0BA8\u0BBF\u0BA9\u0BCD\u0BB1\u0BC1\u0BB5\u0BBF\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1.",
|
|
439
|
+
answerInChat: "\u0BAE\u0BC1\u0BB4\u0BC1\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1.",
|
|
440
|
+
backToChat: "\u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0B95\u0BCD\u0B95\u0BC1\u0BA4\u0BCD \u0BA4\u0BBF\u0BB0\u0BC1\u0BAE\u0BCD\u0BAA\u0BC1",
|
|
441
|
+
busy: "\u0BA8\u0BC7\u0BB0\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0B87\u0BAA\u0BCD\u0BAA\u0BCB\u0BA4\u0BC1 \u0BB5\u0BC7\u0BB1\u0BC1 \u0BAA\u0BAF\u0BA9\u0BCD\u0BAA\u0BBE\u0B9F\u0BCD\u0B9F\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1. \u0B9A\u0BBF\u0BB2 \u0BA8\u0BBF\u0BAE\u0BBF\u0B9F\u0B99\u0BCD\u0B95\u0BB3\u0BBF\u0BB2\u0BCD \u0BAE\u0BC0\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD \u0BAE\u0BC1\u0BAF\u0BB2\u0BB5\u0BC1\u0BAE\u0BCD.",
|
|
442
|
+
limits: "\u0BA8\u0BC7\u0BB0\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BA4\u0BB1\u0BCD\u0BAA\u0BCB\u0BA4\u0BC8\u0B95\u0BCD\u0B95\u0BC1\u0BA4\u0BCD \u0BA4\u0BA9\u0BCD \u0BB5\u0BB0\u0BAE\u0BCD\u0BAA\u0BC8 \u0B8E\u0B9F\u0BCD\u0B9F\u0BBF\u0BB5\u0BBF\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1. \u0BAA\u0BBF\u0BB1\u0B95\u0BC1 \u0BAE\u0BC0\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD \u0BAE\u0BC1\u0BAF\u0BB2\u0BB5\u0BC1\u0BAE\u0BCD.",
|
|
443
|
+
region: "\u0B89\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BAA\u0B95\u0BC1\u0BA4\u0BBF\u0BAF\u0BBF\u0BB2\u0BCD \u0BA8\u0BC7\u0BB0\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0B87\u0BA9\u0BCD\u0BA9\u0BC1\u0BAE\u0BCD \u0B95\u0BBF\u0B9F\u0BC8\u0B95\u0BCD\u0B95\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8.",
|
|
444
|
+
locale: "\u0B87\u0BA8\u0BCD\u0BA4 \u0BAE\u0BCA\u0BB4\u0BBF\u0BAF\u0BBF\u0BB2\u0BCD \u0BA8\u0BC7\u0BB0\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0B87\u0BA9\u0BCD\u0BA9\u0BC1\u0BAE\u0BCD \u0B95\u0BBF\u0B9F\u0BC8\u0B95\u0BCD\u0B95\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8.",
|
|
445
|
+
paused: "\u0BA8\u0BC7\u0BB0\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0B87\u0BAA\u0BCD\u0BAA\u0BCB\u0BA4\u0BC1 \u0B87\u0B9F\u0BC8\u0BA8\u0BBF\u0BB1\u0BC1\u0BA4\u0BCD\u0BA4\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F\u0BC1\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1.",
|
|
446
|
+
notEligible: "\u0BAA\u0BB0\u0BBF\u0BA8\u0BCD\u0BA4\u0BC1\u0BB0\u0BC8\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F \u0B95\u0BC7\u0BB3\u0BCD\u0BB5\u0BBF\u0B95\u0BB3\u0BC1\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD\u0B95\u0BB3\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95 \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BC1\u0BAE\u0BCD.",
|
|
447
|
+
answerClaimed: "\u0B87\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0B8F\u0BB1\u0BCD\u0B95\u0BC6\u0BA9\u0BB5\u0BC7 \u0BAA\u0B9F\u0BBF\u0BA4\u0BCD\u0BA4\u0BC1\u0BB5\u0BBF\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1."
|
|
448
|
+
};
|
|
449
|
+
var STRINGS = {
|
|
450
|
+
en: EN,
|
|
451
|
+
zh: ZH,
|
|
452
|
+
hi: HI,
|
|
453
|
+
es: ES,
|
|
454
|
+
ar: AR,
|
|
455
|
+
ta: TA
|
|
456
|
+
};
|
|
457
|
+
function avatarLiveStrings(locale) {
|
|
458
|
+
return STRINGS[locale] ?? EN;
|
|
459
|
+
}
|
|
460
|
+
var CONSENT = /* @__PURE__ */ new Map([
|
|
461
|
+
[
|
|
462
|
+
"web-consent-2026-11",
|
|
463
|
+
{
|
|
464
|
+
en: {
|
|
465
|
+
title: "Live AI avatar (beta)",
|
|
466
|
+
introProduct: "This is an AI avatar, not a real person. It reads the assistant's first eligible answer to a suggested {product} question aloud for up to a minute. The answer is AI-generated and can be wrong.",
|
|
467
|
+
intro: "This is an AI avatar, not a real person. It reads the assistant's first eligible answer to a suggested question aloud for up to a minute. The answer is AI-generated and can be wrong.",
|
|
468
|
+
processing: "When you start, Cloudflare checks for bots. The answer text goes to Google to create the voice. Your browser connects to LiveKit, which sees your IP address. LiveKit fetches our temporary audio file and passes the voice to Protoface, which makes the avatar video streamed through LiveKit.",
|
|
469
|
+
storage: "Your microphone and camera are not used. We do not record the live session. We temporarily store the generated voice file and keep a record of this consent. The privacy notice explains our safeguards, storage periods and the providers' processing. For people aged eighteen and over.",
|
|
470
|
+
privacyLink: "How we handle this"
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
]
|
|
474
|
+
]);
|
|
475
|
+
function avatarConsentCopy(version, locale) {
|
|
476
|
+
if (!isAvatarLiveLocaleReviewed(locale)) return null;
|
|
477
|
+
return CONSENT.get(version)?.[locale] ?? null;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// src/components/explore-avatar-turnstile.ts
|
|
481
|
+
var SCRIPT_SRC = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
|
|
482
|
+
var SCRIPT_TIMEOUT_MS = 15e3;
|
|
483
|
+
function readApi() {
|
|
484
|
+
if (typeof window === "undefined") return null;
|
|
485
|
+
const api = window.turnstile;
|
|
486
|
+
return api && typeof api.render === "function" && typeof api.remove === "function" ? api : null;
|
|
487
|
+
}
|
|
488
|
+
var loading = null;
|
|
489
|
+
function loadTurnstile() {
|
|
490
|
+
const ready = readApi();
|
|
491
|
+
if (ready) return Promise.resolve(ready);
|
|
492
|
+
if (loading) return loading;
|
|
493
|
+
loading = new Promise((resolve, reject) => {
|
|
494
|
+
const script = document.createElement("script");
|
|
495
|
+
script.src = SCRIPT_SRC;
|
|
496
|
+
script.async = true;
|
|
497
|
+
script.setAttribute("data-boff-explore", "avatar-live-turnstile");
|
|
498
|
+
const fail = () => {
|
|
499
|
+
clearTimeout(timer);
|
|
500
|
+
loading = null;
|
|
501
|
+
script.remove();
|
|
502
|
+
reject(new Error("The bot check could not be loaded."));
|
|
503
|
+
};
|
|
504
|
+
const timer = setTimeout(fail, SCRIPT_TIMEOUT_MS);
|
|
505
|
+
script.addEventListener("load", () => {
|
|
506
|
+
const api = readApi();
|
|
507
|
+
if (!api) {
|
|
508
|
+
fail();
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
clearTimeout(timer);
|
|
512
|
+
resolve(api);
|
|
513
|
+
});
|
|
514
|
+
script.addEventListener("error", fail);
|
|
515
|
+
document.head.appendChild(script);
|
|
516
|
+
});
|
|
517
|
+
return loading;
|
|
518
|
+
}
|
|
519
|
+
function runTurnstile(container, siteKey, action, cData) {
|
|
520
|
+
Promise.resolve(cData).catch(() => void 0);
|
|
521
|
+
let settled = false;
|
|
522
|
+
let cancelled = false;
|
|
523
|
+
let widget = null;
|
|
524
|
+
let rejectToken = () => void 0;
|
|
525
|
+
const removeWidget = () => {
|
|
526
|
+
if (!widget) return;
|
|
527
|
+
const { api, id } = widget;
|
|
528
|
+
widget = null;
|
|
529
|
+
try {
|
|
530
|
+
api.remove(id);
|
|
531
|
+
} catch {
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
const token = new Promise((resolve, reject) => {
|
|
535
|
+
rejectToken = (error) => {
|
|
536
|
+
if (settled) return;
|
|
537
|
+
settled = true;
|
|
538
|
+
reject(error);
|
|
539
|
+
};
|
|
540
|
+
if (!container) {
|
|
541
|
+
rejectToken(new Error("No place for the bot check."));
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
Promise.all([loadTurnstile(), cData]).then(
|
|
545
|
+
([api, binding]) => {
|
|
546
|
+
if (cancelled || settled) return;
|
|
547
|
+
const failed = () => {
|
|
548
|
+
removeWidget();
|
|
549
|
+
rejectToken(new Error("The bot check did not pass."));
|
|
550
|
+
};
|
|
551
|
+
const id = api.render(container, {
|
|
552
|
+
sitekey: siteKey,
|
|
553
|
+
action,
|
|
554
|
+
cData: binding,
|
|
555
|
+
appearance: "interaction-only",
|
|
556
|
+
size: "flexible",
|
|
557
|
+
"response-field": false,
|
|
558
|
+
callback: (value) => {
|
|
559
|
+
if (settled) return;
|
|
560
|
+
settled = true;
|
|
561
|
+
resolve(value);
|
|
562
|
+
},
|
|
563
|
+
"error-callback": () => {
|
|
564
|
+
failed();
|
|
565
|
+
return true;
|
|
566
|
+
},
|
|
567
|
+
"expired-callback": failed,
|
|
568
|
+
"timeout-callback": failed
|
|
569
|
+
});
|
|
570
|
+
if (typeof id === "string" && id !== "") {
|
|
571
|
+
widget = { api, id };
|
|
572
|
+
if (settled) removeWidget();
|
|
573
|
+
} else if (!settled) {
|
|
574
|
+
rejectToken(new Error("The bot check could not start."));
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
(error) => {
|
|
578
|
+
rejectToken(
|
|
579
|
+
error instanceof Error ? error : new Error("The bot check could not be loaded.")
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
);
|
|
583
|
+
});
|
|
584
|
+
token.catch(() => void 0);
|
|
585
|
+
return {
|
|
586
|
+
token,
|
|
587
|
+
cancel: () => {
|
|
588
|
+
cancelled = true;
|
|
589
|
+
removeWidget();
|
|
590
|
+
rejectToken(new Error("The bot check was cancelled."));
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// src/components/explore-fill.ts
|
|
596
|
+
function fill(template, values) {
|
|
597
|
+
return template.replace(/\{(\w+)\}/g, (match, key) => values[key] ?? match);
|
|
598
|
+
}
|
|
599
|
+
function cn(...inputs) {
|
|
600
|
+
return twMerge(clsx(inputs));
|
|
601
|
+
}
|
|
602
|
+
var PRIVACY_HREF = "/privacy#live-avatar";
|
|
603
|
+
var SPEAKER_WAIT_MS = 3e4;
|
|
604
|
+
var END_GRACE_MS = 1e4;
|
|
605
|
+
var CLOCK_TOLERANCE_MS = 5e3;
|
|
606
|
+
var PRIMARY = '[data-avatar-primary], [data-boff-explore="panel-continue-chat"]';
|
|
607
|
+
var IDLE = { name: "idle" };
|
|
608
|
+
var INERT2 = { name: "inert" };
|
|
609
|
+
var REFUSAL_TEXT = {
|
|
610
|
+
busy: "busy",
|
|
611
|
+
limits: "limits",
|
|
612
|
+
region: "region",
|
|
613
|
+
locale: "locale",
|
|
614
|
+
paused: "paused",
|
|
615
|
+
not_eligible: "notEligible",
|
|
616
|
+
answer_claimed: "answerClaimed"
|
|
617
|
+
};
|
|
618
|
+
var WAITING = { answerId: null, startOver: false };
|
|
619
|
+
var START_OVER = { answerId: null, startOver: true };
|
|
620
|
+
function firstExchange(messages) {
|
|
621
|
+
if (!messages || messages.length === 0) return WAITING;
|
|
622
|
+
if (messages.length > 2) return START_OVER;
|
|
623
|
+
const [question, answer] = messages;
|
|
624
|
+
if (question?.role !== "USER") return START_OVER;
|
|
625
|
+
if (answer === void 0) return WAITING;
|
|
626
|
+
if (answer.role !== "ASSISTANT") return START_OVER;
|
|
627
|
+
if (answer.status === "PENDING" || answer.status === "RUNNING") {
|
|
628
|
+
return WAITING;
|
|
629
|
+
}
|
|
630
|
+
if (answer.status !== "COMPLETED" || answer.content.trim() === "" || !isAvatarMessageId(answer.id)) {
|
|
631
|
+
return START_OVER;
|
|
632
|
+
}
|
|
633
|
+
return { answerId: answer.id, startOver: false };
|
|
634
|
+
}
|
|
635
|
+
function offering(offer, messageId, claimed) {
|
|
636
|
+
return claimed.has(messageId) ? { name: "refused", reason: "answer_claimed" } : { name: "consent", offer, messageId };
|
|
637
|
+
}
|
|
638
|
+
async function loadModule(load) {
|
|
639
|
+
const loaded = await load();
|
|
640
|
+
const join = loaded !== null && typeof loaded === "object" ? loaded.joinReceiveOnly : void 0;
|
|
641
|
+
if (typeof join !== "function") {
|
|
642
|
+
throw new Error("The live avatar module could not be loaded.");
|
|
643
|
+
}
|
|
644
|
+
return loaded;
|
|
645
|
+
}
|
|
646
|
+
function clock(seconds) {
|
|
647
|
+
const whole = Math.max(0, Math.ceil(seconds));
|
|
648
|
+
return `${Math.floor(whole / 60)}:${String(whole % 60).padStart(2, "0")}`;
|
|
649
|
+
}
|
|
650
|
+
function AvatarLiveView({
|
|
651
|
+
live,
|
|
652
|
+
active,
|
|
653
|
+
locale,
|
|
654
|
+
productName,
|
|
655
|
+
still,
|
|
656
|
+
fallback,
|
|
657
|
+
onContinueInChat,
|
|
658
|
+
continueInChat,
|
|
659
|
+
off,
|
|
660
|
+
base,
|
|
661
|
+
headers,
|
|
662
|
+
conversation
|
|
663
|
+
}) {
|
|
664
|
+
const S = avatarLiveStrings(locale);
|
|
665
|
+
const uid = useId();
|
|
666
|
+
const consentTitleId = `${uid}-consent`;
|
|
667
|
+
const { answerId, startOver } = firstExchange(conversation?.messages);
|
|
668
|
+
const [phase, setPhase] = useState(IDLE);
|
|
669
|
+
const [blocked, setBlocked] = useState(false);
|
|
670
|
+
const [now, setNow] = useState(() => Date.now());
|
|
671
|
+
const phaseRef = useRef(IDLE);
|
|
672
|
+
const latest = useRef({ live, locale, base, headers, answerId, off });
|
|
673
|
+
latest.current = { live, locale, base, headers, answerId, off };
|
|
674
|
+
const conversationRef = useRef(conversation);
|
|
675
|
+
conversationRef.current = conversation;
|
|
676
|
+
const checkRef = useRef(null);
|
|
677
|
+
const askedLocale = useRef(locale);
|
|
678
|
+
const startRef = useRef(null);
|
|
679
|
+
const sessionRef = useRef(null);
|
|
680
|
+
const claimedRef = useRef(/* @__PURE__ */ new Set());
|
|
681
|
+
const rootRef = useRef(null);
|
|
682
|
+
const videoRef = useRef(null);
|
|
683
|
+
const audioRef = useRef(null);
|
|
684
|
+
const botBoxRef = useRef(null);
|
|
685
|
+
const go = useCallback((next) => {
|
|
686
|
+
phaseRef.current = next;
|
|
687
|
+
setPhase(next);
|
|
688
|
+
}, []);
|
|
689
|
+
const finish = useCallback(
|
|
690
|
+
(how, reason) => {
|
|
691
|
+
const handle = sessionRef.current;
|
|
692
|
+
if (!handle || handle.done) return;
|
|
693
|
+
handle.done = true;
|
|
694
|
+
sessionRef.current = null;
|
|
695
|
+
if (reason !== null) {
|
|
696
|
+
sendEnd({
|
|
697
|
+
base: handle.base,
|
|
698
|
+
headers: handle.headers,
|
|
699
|
+
sessionId: handle.session.sessionId,
|
|
700
|
+
secret: handle.session.endSecret,
|
|
701
|
+
reason
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
const room = handle.joined;
|
|
705
|
+
handle.joined = null;
|
|
706
|
+
if (room) void room.leave().catch(() => void 0);
|
|
707
|
+
setBlocked(false);
|
|
708
|
+
go({ name: "ended", how });
|
|
709
|
+
},
|
|
710
|
+
[go]
|
|
711
|
+
);
|
|
712
|
+
const cancelStart = useCallback((reason) => {
|
|
713
|
+
const run = startRef.current;
|
|
714
|
+
if (!run) return;
|
|
715
|
+
startRef.current = null;
|
|
716
|
+
run.cancelled = reason;
|
|
717
|
+
run.turnstile?.cancel();
|
|
718
|
+
}, []);
|
|
719
|
+
const check = useCallback(() => {
|
|
720
|
+
const { live: live2, locale: locale2, base: base2, headers: headers2, off: off2 } = latest.current;
|
|
721
|
+
checkRef.current?.abort();
|
|
722
|
+
checkRef.current = null;
|
|
723
|
+
askedLocale.current = locale2;
|
|
724
|
+
if (off2 || base2 === null) {
|
|
725
|
+
go(INERT2);
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
const controller = new AbortController();
|
|
729
|
+
checkRef.current = controller;
|
|
730
|
+
go({ name: "checking" });
|
|
731
|
+
void requestAvailability({
|
|
732
|
+
base: base2,
|
|
733
|
+
headers: headers2,
|
|
734
|
+
site: live2.site,
|
|
735
|
+
locale: locale2,
|
|
736
|
+
signal: controller.signal
|
|
737
|
+
}).then((result) => {
|
|
738
|
+
if (checkRef.current !== controller) return;
|
|
739
|
+
checkRef.current = null;
|
|
740
|
+
if (result.kind === "inert") {
|
|
741
|
+
go(INERT2);
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
if (result.kind === "refused") {
|
|
745
|
+
go({ name: "refused", reason: result.reason });
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
const copy = avatarConsentCopy(result.offer.consentVersion, locale2);
|
|
749
|
+
if (copy === null) {
|
|
750
|
+
go(INERT2);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
const offer = { ...result.offer, locale: locale2, copy };
|
|
754
|
+
const messageId = latest.current.answerId;
|
|
755
|
+
go(
|
|
756
|
+
messageId === null ? { name: "needs-answer", offer } : offering(offer, messageId, claimedRef.current)
|
|
757
|
+
);
|
|
758
|
+
});
|
|
759
|
+
}, [go]);
|
|
760
|
+
const begin = useCallback(
|
|
761
|
+
(session, module, offer, target) => {
|
|
762
|
+
const received = Date.now();
|
|
763
|
+
const longest = offer.maxSessionSeconds * 1e3;
|
|
764
|
+
const ahead = session.endsAt - received;
|
|
765
|
+
const endsAt = ahead > 0 && ahead <= longest + CLOCK_TOLERANCE_MS ? session.endsAt : received + longest;
|
|
766
|
+
sessionRef.current = {
|
|
767
|
+
session,
|
|
768
|
+
base: target.base,
|
|
769
|
+
headers: target.headers,
|
|
770
|
+
module,
|
|
771
|
+
room: null,
|
|
772
|
+
joined: null,
|
|
773
|
+
endsAt,
|
|
774
|
+
speakerArrived: false,
|
|
775
|
+
done: false
|
|
776
|
+
};
|
|
777
|
+
setBlocked(false);
|
|
778
|
+
setNow(received);
|
|
779
|
+
go({
|
|
780
|
+
name: "pending",
|
|
781
|
+
session: { endsAt, captions: session.captions }
|
|
782
|
+
});
|
|
783
|
+
},
|
|
784
|
+
[go]
|
|
785
|
+
);
|
|
786
|
+
const settle = useCallback(
|
|
787
|
+
(result, run, context) => {
|
|
788
|
+
const created = result.kind === "created" ? {
|
|
789
|
+
sessionId: result.session.sessionId,
|
|
790
|
+
secret: result.session.endSecret
|
|
791
|
+
} : result.kind === "unusable" ? { sessionId: result.sessionId, secret: result.endSecret } : null;
|
|
792
|
+
const claimed = created !== null || result.kind === "refused" && result.reason === "answer_claimed";
|
|
793
|
+
if (claimed) claimedRef.current.add(context.messageId);
|
|
794
|
+
if (run.cancelled !== null) {
|
|
795
|
+
if (created !== null) {
|
|
796
|
+
sendEnd({
|
|
797
|
+
base: context.base,
|
|
798
|
+
headers: context.headers,
|
|
799
|
+
sessionId: created.sessionId,
|
|
800
|
+
secret: created.secret,
|
|
801
|
+
reason: run.cancelled
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
const shown = phaseRef.current;
|
|
805
|
+
if (claimed && shown.name === "consent" && shown.messageId === context.messageId) {
|
|
806
|
+
go({ name: "refused", reason: "answer_claimed" });
|
|
807
|
+
}
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
switch (result.kind) {
|
|
811
|
+
case "created":
|
|
812
|
+
begin(result.session, context.module, context.offer, context);
|
|
813
|
+
return;
|
|
814
|
+
case "unusable":
|
|
815
|
+
sendEnd({
|
|
816
|
+
base: context.base,
|
|
817
|
+
headers: context.headers,
|
|
818
|
+
sessionId: result.sessionId,
|
|
819
|
+
secret: result.endSecret,
|
|
820
|
+
reason: "client_error"
|
|
821
|
+
});
|
|
822
|
+
go({ name: "ended", how: "failed" });
|
|
823
|
+
return;
|
|
824
|
+
case "refused":
|
|
825
|
+
go({ name: "refused", reason: result.reason });
|
|
826
|
+
return;
|
|
827
|
+
case "consent":
|
|
828
|
+
check();
|
|
829
|
+
return;
|
|
830
|
+
case "inert":
|
|
831
|
+
go(INERT2);
|
|
832
|
+
return;
|
|
833
|
+
case "failed":
|
|
834
|
+
go({ name: "ended", how: "failed" });
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
},
|
|
838
|
+
[begin, check, go]
|
|
839
|
+
);
|
|
840
|
+
const start = useCallback(() => {
|
|
841
|
+
const current = phaseRef.current;
|
|
842
|
+
if (current.name !== "consent") return;
|
|
843
|
+
const { live: live2, base: base2, headers: headers2 } = latest.current;
|
|
844
|
+
if (base2 === null) return;
|
|
845
|
+
if (!canBindBotCheck()) {
|
|
846
|
+
go(INERT2);
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
const { offer, messageId } = current;
|
|
850
|
+
const conversationToken = conversationRef.current?.getConversationToken() ?? null;
|
|
851
|
+
const idempotencyKey = newIdempotencyKey();
|
|
852
|
+
if (conversationToken === null || !isAvatarConversationToken(conversationToken) || idempotencyKey === null) {
|
|
853
|
+
go({ name: "ended", how: "failed" });
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
const body2 = {
|
|
857
|
+
site: live2.site,
|
|
858
|
+
locale: offer.locale,
|
|
859
|
+
conversationToken,
|
|
860
|
+
messageId,
|
|
861
|
+
consentVersion: offer.consentVersion,
|
|
862
|
+
manifestVersion: live2.manifestVersion
|
|
863
|
+
};
|
|
864
|
+
const run = { cancelled: null, turnstile: null };
|
|
865
|
+
startRef.current = run;
|
|
866
|
+
go({ name: "bot-check", offer, messageId });
|
|
867
|
+
const module = loadModule(live2.load);
|
|
868
|
+
const turnstile = runTurnstile(
|
|
869
|
+
botBoxRef.current,
|
|
870
|
+
live2.turnstileSiteKey,
|
|
871
|
+
"avatar_start",
|
|
872
|
+
avatarBotBinding(body2)
|
|
873
|
+
);
|
|
874
|
+
run.turnstile = turnstile;
|
|
875
|
+
void (async () => {
|
|
876
|
+
let loaded;
|
|
877
|
+
let botToken;
|
|
878
|
+
try {
|
|
879
|
+
[loaded, botToken] = await Promise.all([module, turnstile.token]);
|
|
880
|
+
} catch {
|
|
881
|
+
turnstile.cancel();
|
|
882
|
+
if (run.cancelled === null) {
|
|
883
|
+
startRef.current = null;
|
|
884
|
+
go(INERT2);
|
|
885
|
+
}
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
turnstile.cancel();
|
|
889
|
+
if (run.cancelled !== null) return;
|
|
890
|
+
go({ name: "creating" });
|
|
891
|
+
const result = await requestCreate({
|
|
892
|
+
base: base2,
|
|
893
|
+
headers: headers2,
|
|
894
|
+
idempotencyKey,
|
|
895
|
+
botToken,
|
|
896
|
+
body: body2
|
|
897
|
+
});
|
|
898
|
+
if (startRef.current === run) startRef.current = null;
|
|
899
|
+
settle(result, run, { module: loaded, offer, messageId, base: base2, headers: headers2 });
|
|
900
|
+
})();
|
|
901
|
+
}, [go, settle]);
|
|
902
|
+
const stop = useCallback(() => {
|
|
903
|
+
const current = phaseRef.current;
|
|
904
|
+
if (current.name === "bot-check") {
|
|
905
|
+
cancelStart("user");
|
|
906
|
+
go(offering(current.offer, current.messageId, claimedRef.current));
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
if (current.name === "creating") {
|
|
910
|
+
cancelStart("user");
|
|
911
|
+
go({ name: "ended", how: "stopped" });
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
if (current.name === "pending" || current.name === "live") {
|
|
915
|
+
finish("stopped", "user");
|
|
916
|
+
}
|
|
917
|
+
}, [cancelStart, finish, go]);
|
|
918
|
+
const leave = useCallback(
|
|
919
|
+
(reason) => {
|
|
920
|
+
const current = phaseRef.current;
|
|
921
|
+
const how = reason === "user" ? "stopped" : "interrupted";
|
|
922
|
+
switch (current.name) {
|
|
923
|
+
case "checking": {
|
|
924
|
+
if (reason !== "user") return;
|
|
925
|
+
const controller = checkRef.current;
|
|
926
|
+
checkRef.current = null;
|
|
927
|
+
controller?.abort();
|
|
928
|
+
go(IDLE);
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
case "bot-check":
|
|
932
|
+
cancelStart(reason);
|
|
933
|
+
go(
|
|
934
|
+
reason === "user" ? IDLE : offering(current.offer, current.messageId, claimedRef.current)
|
|
935
|
+
);
|
|
936
|
+
return;
|
|
937
|
+
case "creating":
|
|
938
|
+
cancelStart(reason);
|
|
939
|
+
go({ name: "ended", how });
|
|
940
|
+
return;
|
|
941
|
+
case "pending":
|
|
942
|
+
case "live":
|
|
943
|
+
finish(how, reason);
|
|
944
|
+
return;
|
|
945
|
+
default:
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
},
|
|
949
|
+
[cancelStart, finish, go]
|
|
950
|
+
);
|
|
951
|
+
const showing = active && !off;
|
|
952
|
+
useEffect(() => {
|
|
953
|
+
if (!showing) {
|
|
954
|
+
leave("user");
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
const current = phaseRef.current;
|
|
958
|
+
const { answerId: offered } = latest.current;
|
|
959
|
+
const used = offered !== null && claimedRef.current.has(offered);
|
|
960
|
+
if (current.name === "ended" && used) return;
|
|
961
|
+
if (current.name === "refused" && current.reason === "answer_claimed" && used) {
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
if (current.name === "idle" || current.name === "inert" || current.name === "refused" || current.name === "needs-answer" || current.name === "consent" || current.name === "ended" || // Asking, with nothing actually asking: React's strict mode unmounts and mounts a component again with the state it
|
|
965
|
+
// left, and the unmount in between dropped the question. (This view mounts already showing, the first time the
|
|
966
|
+
// Avatar view opens.)
|
|
967
|
+
current.name === "checking" && checkRef.current === null) {
|
|
968
|
+
check();
|
|
969
|
+
}
|
|
970
|
+
}, [showing, check, leave]);
|
|
971
|
+
useEffect(() => {
|
|
972
|
+
if (askedLocale.current === locale) return;
|
|
973
|
+
askedLocale.current = locale;
|
|
974
|
+
const current = phaseRef.current.name;
|
|
975
|
+
if (current !== "checking" && current !== "needs-answer" && current !== "consent" && current !== "refused" && current !== "inert") {
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
if (active) {
|
|
979
|
+
check();
|
|
980
|
+
} else {
|
|
981
|
+
checkRef.current?.abort();
|
|
982
|
+
checkRef.current = null;
|
|
983
|
+
go(IDLE);
|
|
984
|
+
}
|
|
985
|
+
}, [active, check, go, locale]);
|
|
986
|
+
useEffect(() => {
|
|
987
|
+
const current = phaseRef.current;
|
|
988
|
+
if (current.name === "needs-answer" && answerId !== null) {
|
|
989
|
+
go({ name: "consent", offer: current.offer, messageId: answerId });
|
|
990
|
+
} else if (current.name === "consent" && current.messageId !== answerId) {
|
|
991
|
+
go(
|
|
992
|
+
answerId === null ? { name: "needs-answer", offer: current.offer } : { ...current, messageId: answerId }
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
}, [answerId, go]);
|
|
996
|
+
const onRoomEvent = useCallback(
|
|
997
|
+
(handle, event) => {
|
|
998
|
+
if (handle.done || sessionRef.current !== handle) return;
|
|
999
|
+
const wasLive = phaseRef.current.name === "live";
|
|
1000
|
+
switch (event.type) {
|
|
1001
|
+
case "speaker-track":
|
|
1002
|
+
if (event.kind === "video") handle.speakerArrived = true;
|
|
1003
|
+
return;
|
|
1004
|
+
case "playback-blocked":
|
|
1005
|
+
setBlocked(event.blocked);
|
|
1006
|
+
return;
|
|
1007
|
+
case "speaker-left":
|
|
1008
|
+
finish(
|
|
1009
|
+
wasLive ? "finished" : "failed",
|
|
1010
|
+
wasLive ? null : "client_error"
|
|
1011
|
+
);
|
|
1012
|
+
return;
|
|
1013
|
+
case "disconnected":
|
|
1014
|
+
if (event.byServer) finish(wasLive ? "finished" : "failed", null);
|
|
1015
|
+
else finish(wasLive ? "interrupted" : "failed", "client_error");
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
},
|
|
1019
|
+
[finish]
|
|
1020
|
+
);
|
|
1021
|
+
useEffect(() => {
|
|
1022
|
+
if (phase.name !== "pending") return;
|
|
1023
|
+
const handle = sessionRef.current;
|
|
1024
|
+
if (!handle || handle.room !== null || handle.done) return;
|
|
1025
|
+
const video = videoRef.current;
|
|
1026
|
+
const audio = audioRef.current;
|
|
1027
|
+
if (!video || !audio) {
|
|
1028
|
+
finish("failed", "client_error");
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
let joining;
|
|
1032
|
+
try {
|
|
1033
|
+
joining = handle.module.joinReceiveOnly({
|
|
1034
|
+
url: handle.session.join.livekitUrl,
|
|
1035
|
+
token: handle.session.join.participantToken,
|
|
1036
|
+
workerIdentity: handle.session.join.workerIdentity,
|
|
1037
|
+
video,
|
|
1038
|
+
audio,
|
|
1039
|
+
onEvent: (event) => onRoomEvent(handle, event)
|
|
1040
|
+
});
|
|
1041
|
+
} catch (error) {
|
|
1042
|
+
joining = Promise.reject(error);
|
|
1043
|
+
}
|
|
1044
|
+
handle.room = joining.then(
|
|
1045
|
+
(room) => {
|
|
1046
|
+
if (handle.done) {
|
|
1047
|
+
void room.leave().catch(() => void 0);
|
|
1048
|
+
return null;
|
|
1049
|
+
}
|
|
1050
|
+
handle.joined = room;
|
|
1051
|
+
return room;
|
|
1052
|
+
},
|
|
1053
|
+
() => {
|
|
1054
|
+
if (!handle.done) finish("failed", "client_error");
|
|
1055
|
+
return null;
|
|
1056
|
+
}
|
|
1057
|
+
);
|
|
1058
|
+
}, [phase.name, finish, onRoomEvent]);
|
|
1059
|
+
useEffect(() => {
|
|
1060
|
+
if (phase.name !== "pending" && phase.name !== "live") return;
|
|
1061
|
+
const handle = sessionRef.current;
|
|
1062
|
+
if (!handle) return;
|
|
1063
|
+
const timers = [];
|
|
1064
|
+
if (phase.name === "pending") {
|
|
1065
|
+
timers.push(
|
|
1066
|
+
setTimeout(() => {
|
|
1067
|
+
if (sessionRef.current === handle && !handle.speakerArrived) {
|
|
1068
|
+
finish("failed", "client_error");
|
|
1069
|
+
}
|
|
1070
|
+
}, SPEAKER_WAIT_MS)
|
|
1071
|
+
);
|
|
1072
|
+
}
|
|
1073
|
+
timers.push(
|
|
1074
|
+
setTimeout(
|
|
1075
|
+
() => {
|
|
1076
|
+
if (sessionRef.current !== handle) return;
|
|
1077
|
+
finish(
|
|
1078
|
+
phaseRef.current.name === "live" ? "finished" : "failed",
|
|
1079
|
+
"client_error"
|
|
1080
|
+
);
|
|
1081
|
+
},
|
|
1082
|
+
Math.max(0, handle.endsAt + END_GRACE_MS - Date.now())
|
|
1083
|
+
)
|
|
1084
|
+
);
|
|
1085
|
+
return () => timers.forEach((timer) => clearTimeout(timer));
|
|
1086
|
+
}, [phase.name, finish]);
|
|
1087
|
+
const ticking = phase.name === "pending" || phase.name === "live";
|
|
1088
|
+
useEffect(() => {
|
|
1089
|
+
if (!ticking) return;
|
|
1090
|
+
const timer = setInterval(() => setNow(Date.now()), 1e3);
|
|
1091
|
+
return () => clearInterval(timer);
|
|
1092
|
+
}, [ticking]);
|
|
1093
|
+
useEffect(() => {
|
|
1094
|
+
if (typeof window === "undefined") return;
|
|
1095
|
+
const onPageHide = () => leave("page_hide");
|
|
1096
|
+
const onVisibility = () => {
|
|
1097
|
+
if (document.visibilityState === "hidden") leave("page_hide");
|
|
1098
|
+
};
|
|
1099
|
+
window.addEventListener("pagehide", onPageHide);
|
|
1100
|
+
document.addEventListener("visibilitychange", onVisibility);
|
|
1101
|
+
return () => {
|
|
1102
|
+
window.removeEventListener("pagehide", onPageHide);
|
|
1103
|
+
document.removeEventListener("visibilitychange", onVisibility);
|
|
1104
|
+
};
|
|
1105
|
+
}, [leave]);
|
|
1106
|
+
useEffect(
|
|
1107
|
+
() => () => {
|
|
1108
|
+
const controller = checkRef.current;
|
|
1109
|
+
checkRef.current = null;
|
|
1110
|
+
controller?.abort();
|
|
1111
|
+
const run = startRef.current;
|
|
1112
|
+
startRef.current = null;
|
|
1113
|
+
if (run) {
|
|
1114
|
+
run.cancelled = "user";
|
|
1115
|
+
run.turnstile?.cancel();
|
|
1116
|
+
}
|
|
1117
|
+
const handle = sessionRef.current;
|
|
1118
|
+
sessionRef.current = null;
|
|
1119
|
+
if (handle && !handle.done) {
|
|
1120
|
+
handle.done = true;
|
|
1121
|
+
sendEnd({
|
|
1122
|
+
base: handle.base,
|
|
1123
|
+
headers: handle.headers,
|
|
1124
|
+
sessionId: handle.session.sessionId,
|
|
1125
|
+
secret: handle.session.endSecret,
|
|
1126
|
+
reason: "user"
|
|
1127
|
+
});
|
|
1128
|
+
if (handle.joined) void handle.joined.leave().catch(() => void 0);
|
|
1129
|
+
}
|
|
1130
|
+
},
|
|
1131
|
+
[]
|
|
1132
|
+
);
|
|
1133
|
+
useEffect(() => {
|
|
1134
|
+
if (!active) return;
|
|
1135
|
+
const focused = document.activeElement;
|
|
1136
|
+
if (focused && focused !== document.body && focused.isConnected) return;
|
|
1137
|
+
rootRef.current?.querySelector(PRIMARY)?.focus();
|
|
1138
|
+
}, [active, phase.name, blocked]);
|
|
1139
|
+
const onPlaying = useCallback(() => {
|
|
1140
|
+
const current = phaseRef.current;
|
|
1141
|
+
const handle = sessionRef.current;
|
|
1142
|
+
if (current.name !== "pending" || !handle) return;
|
|
1143
|
+
handle.speakerArrived = true;
|
|
1144
|
+
go({ name: "live", session: current.session });
|
|
1145
|
+
}, [go]);
|
|
1146
|
+
const enablePlayback = useCallback(() => {
|
|
1147
|
+
void sessionRef.current?.joined?.enablePlayback().catch(() => void 0);
|
|
1148
|
+
}, []);
|
|
1149
|
+
if (off || phase.name === "inert") {
|
|
1150
|
+
return (
|
|
1151
|
+
// `display: contents`: the earlier Avatar view's picture, line and button lay out in the pane exactly as before.
|
|
1152
|
+
/* @__PURE__ */ jsx(
|
|
1153
|
+
"div",
|
|
1154
|
+
{
|
|
1155
|
+
ref: rootRef,
|
|
1156
|
+
style: { display: "contents" },
|
|
1157
|
+
"data-boff-explore": "avatar-live",
|
|
1158
|
+
"data-phase": off ? "off" : "inert",
|
|
1159
|
+
children: fallback
|
|
1160
|
+
}
|
|
1161
|
+
)
|
|
1162
|
+
);
|
|
1163
|
+
}
|
|
1164
|
+
const primaryButton = "inline-flex 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";
|
|
1165
|
+
const secondaryButton = "inline-flex 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";
|
|
1166
|
+
const stillPicture = (small) => /* @__PURE__ */ jsx(
|
|
1167
|
+
"img",
|
|
1168
|
+
{
|
|
1169
|
+
src: still.src,
|
|
1170
|
+
alt: still.alt,
|
|
1171
|
+
decoding: "async",
|
|
1172
|
+
width: small ? 64 : 128,
|
|
1173
|
+
height: small ? 64 : 128,
|
|
1174
|
+
className: cn(
|
|
1175
|
+
"shrink-0 rounded-full border border-border object-cover shadow-sm",
|
|
1176
|
+
small ? "h-16 w-16" : "h-32 w-32"
|
|
1177
|
+
)
|
|
1178
|
+
}
|
|
1179
|
+
);
|
|
1180
|
+
const continueButton = /* @__PURE__ */ jsx(
|
|
1181
|
+
"button",
|
|
1182
|
+
{
|
|
1183
|
+
type: "button",
|
|
1184
|
+
"data-avatar-primary": "",
|
|
1185
|
+
"data-boff-explore": "avatar-live-continue",
|
|
1186
|
+
className: primaryButton,
|
|
1187
|
+
onClick: onContinueInChat,
|
|
1188
|
+
children: continueInChat
|
|
1189
|
+
}
|
|
1190
|
+
);
|
|
1191
|
+
const stopButton = /* @__PURE__ */ jsx(
|
|
1192
|
+
"button",
|
|
1193
|
+
{
|
|
1194
|
+
type: "button",
|
|
1195
|
+
"data-avatar-primary": "",
|
|
1196
|
+
"data-boff-explore": "avatar-live-stop",
|
|
1197
|
+
className: secondaryButton,
|
|
1198
|
+
onClick: stop,
|
|
1199
|
+
children: S.stop
|
|
1200
|
+
}
|
|
1201
|
+
);
|
|
1202
|
+
const headline = {
|
|
1203
|
+
idle: "",
|
|
1204
|
+
checking: S.checking,
|
|
1205
|
+
inert: "",
|
|
1206
|
+
"needs-answer": startOver ? S.startNewChat : S.needsAnswer,
|
|
1207
|
+
consent: "",
|
|
1208
|
+
refused: phase.name === "refused" ? S[REFUSAL_TEXT[phase.reason]] : "",
|
|
1209
|
+
"bot-check": S.botCheck,
|
|
1210
|
+
creating: S.connecting,
|
|
1211
|
+
pending: S.connecting,
|
|
1212
|
+
live: S.speaking,
|
|
1213
|
+
ended: phase.name === "ended" ? S[phase.how] : ""
|
|
1214
|
+
};
|
|
1215
|
+
const outcome = phase.name === "needs-answer" || phase.name === "refused" || phase.name === "ended";
|
|
1216
|
+
let top = null;
|
|
1217
|
+
let media = stillPicture(false);
|
|
1218
|
+
let body = null;
|
|
1219
|
+
let actions = null;
|
|
1220
|
+
switch (phase.name) {
|
|
1221
|
+
case "needs-answer":
|
|
1222
|
+
body = /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: startOver ? S.startNewChatDetail : S.needsAnswerDetail });
|
|
1223
|
+
actions = continueButton;
|
|
1224
|
+
break;
|
|
1225
|
+
case "consent": {
|
|
1226
|
+
const { copy } = phase.offer;
|
|
1227
|
+
media = stillPicture(true);
|
|
1228
|
+
body = /* @__PURE__ */ jsxs(
|
|
1229
|
+
"section",
|
|
1230
|
+
{
|
|
1231
|
+
"aria-labelledby": consentTitleId,
|
|
1232
|
+
"data-boff-explore": "avatar-live-consent",
|
|
1233
|
+
className: "w-full max-w-sm rounded-lg border border-border p-4 text-start",
|
|
1234
|
+
children: [
|
|
1235
|
+
/* @__PURE__ */ jsx("h2", { id: consentTitleId, className: "text-base font-semibold", children: copy.title }),
|
|
1236
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm", children: productName === null ? copy.intro : fill(copy.introProduct, { product: productName }) }),
|
|
1237
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: copy.processing }),
|
|
1238
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: copy.storage }),
|
|
1239
|
+
/* @__PURE__ */ jsxs(
|
|
1240
|
+
"a",
|
|
1241
|
+
{
|
|
1242
|
+
href: PRIVACY_HREF,
|
|
1243
|
+
target: "_blank",
|
|
1244
|
+
rel: "noopener noreferrer",
|
|
1245
|
+
"data-boff-explore": "avatar-live-privacy",
|
|
1246
|
+
className: "mt-2 inline-block text-sm font-medium text-primary underline underline-offset-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
1247
|
+
children: [
|
|
1248
|
+
copy.privacyLink,
|
|
1249
|
+
/* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
|
|
1250
|
+
" ",
|
|
1251
|
+
S.opensInNewTab
|
|
1252
|
+
] })
|
|
1253
|
+
]
|
|
1254
|
+
}
|
|
1255
|
+
)
|
|
1256
|
+
]
|
|
1257
|
+
}
|
|
1258
|
+
);
|
|
1259
|
+
actions = /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2", children: [
|
|
1260
|
+
/* @__PURE__ */ jsx(
|
|
1261
|
+
"button",
|
|
1262
|
+
{
|
|
1263
|
+
type: "button",
|
|
1264
|
+
"data-avatar-primary": "",
|
|
1265
|
+
"data-boff-explore": "avatar-live-start",
|
|
1266
|
+
className: primaryButton,
|
|
1267
|
+
onClick: start,
|
|
1268
|
+
children: S.start
|
|
1269
|
+
}
|
|
1270
|
+
),
|
|
1271
|
+
/* @__PURE__ */ jsx(
|
|
1272
|
+
"button",
|
|
1273
|
+
{
|
|
1274
|
+
type: "button",
|
|
1275
|
+
"data-boff-explore": "avatar-live-not-now",
|
|
1276
|
+
className: secondaryButton,
|
|
1277
|
+
onClick: onContinueInChat,
|
|
1278
|
+
children: S.notNow
|
|
1279
|
+
}
|
|
1280
|
+
)
|
|
1281
|
+
] });
|
|
1282
|
+
break;
|
|
1283
|
+
}
|
|
1284
|
+
case "refused":
|
|
1285
|
+
actions = continueButton;
|
|
1286
|
+
break;
|
|
1287
|
+
case "bot-check":
|
|
1288
|
+
case "creating":
|
|
1289
|
+
actions = stopButton;
|
|
1290
|
+
break;
|
|
1291
|
+
case "pending":
|
|
1292
|
+
case "live": {
|
|
1293
|
+
const playing = phase.name === "live";
|
|
1294
|
+
const left = Math.max(0, (phase.session.endsAt - now) / 1e3);
|
|
1295
|
+
top = /* @__PURE__ */ jsx(
|
|
1296
|
+
"p",
|
|
1297
|
+
{
|
|
1298
|
+
"data-boff-explore": "avatar-live-badge",
|
|
1299
|
+
className: "rounded-full bg-muted px-3 py-1 text-xs font-medium text-foreground",
|
|
1300
|
+
children: S.badge
|
|
1301
|
+
}
|
|
1302
|
+
);
|
|
1303
|
+
media = /* @__PURE__ */ jsxs("div", { className: "relative h-48 w-48 shrink-0 overflow-hidden rounded-xl border border-border bg-muted", children: [
|
|
1304
|
+
/* @__PURE__ */ jsx(
|
|
1305
|
+
"video",
|
|
1306
|
+
{
|
|
1307
|
+
ref: videoRef,
|
|
1308
|
+
"aria-label": S.video,
|
|
1309
|
+
"data-boff-explore": "avatar-live-video",
|
|
1310
|
+
playsInline: true,
|
|
1311
|
+
autoPlay: true,
|
|
1312
|
+
muted: true,
|
|
1313
|
+
onPlaying,
|
|
1314
|
+
className: cn(
|
|
1315
|
+
"h-full w-full object-cover",
|
|
1316
|
+
!playing && "opacity-0"
|
|
1317
|
+
)
|
|
1318
|
+
}
|
|
1319
|
+
),
|
|
1320
|
+
playing ? null : /* @__PURE__ */ jsx(
|
|
1321
|
+
"img",
|
|
1322
|
+
{
|
|
1323
|
+
src: still.src,
|
|
1324
|
+
alt: "",
|
|
1325
|
+
decoding: "async",
|
|
1326
|
+
className: "absolute inset-0 h-full w-full object-cover"
|
|
1327
|
+
}
|
|
1328
|
+
),
|
|
1329
|
+
/* @__PURE__ */ jsx("audio", { ref: audioRef, "data-boff-explore": "avatar-live-audio" })
|
|
1330
|
+
] });
|
|
1331
|
+
body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1332
|
+
/* @__PURE__ */ jsx(
|
|
1333
|
+
"section",
|
|
1334
|
+
{
|
|
1335
|
+
"aria-label": S.captions,
|
|
1336
|
+
"data-boff-explore": "avatar-live-captions",
|
|
1337
|
+
lang: phase.session.captions.locale || void 0,
|
|
1338
|
+
dir: "auto",
|
|
1339
|
+
tabIndex: 0,
|
|
1340
|
+
className: "max-h-32 w-full max-w-sm overflow-y-auto rounded-lg bg-muted p-3 text-start text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
1341
|
+
children: phase.session.captions.text
|
|
1342
|
+
}
|
|
1343
|
+
),
|
|
1344
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: S.answerInChat }),
|
|
1345
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm tabular-nums text-muted-foreground", children: [
|
|
1346
|
+
S.timeLeft,
|
|
1347
|
+
" ",
|
|
1348
|
+
/* @__PURE__ */ jsx("span", { role: "timer", "data-boff-explore": "avatar-live-countdown", children: clock(left) })
|
|
1349
|
+
] })
|
|
1350
|
+
] });
|
|
1351
|
+
actions = /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2", children: [
|
|
1352
|
+
blocked ? /* @__PURE__ */ jsx(
|
|
1353
|
+
"button",
|
|
1354
|
+
{
|
|
1355
|
+
type: "button",
|
|
1356
|
+
"data-boff-explore": "avatar-live-sound",
|
|
1357
|
+
className: primaryButton,
|
|
1358
|
+
onClick: enablePlayback,
|
|
1359
|
+
children: S.tapForSound
|
|
1360
|
+
}
|
|
1361
|
+
) : null,
|
|
1362
|
+
stopButton,
|
|
1363
|
+
/* @__PURE__ */ jsx(
|
|
1364
|
+
"button",
|
|
1365
|
+
{
|
|
1366
|
+
type: "button",
|
|
1367
|
+
"data-boff-explore": "avatar-live-continue",
|
|
1368
|
+
className: secondaryButton,
|
|
1369
|
+
onClick: onContinueInChat,
|
|
1370
|
+
children: continueInChat
|
|
1371
|
+
}
|
|
1372
|
+
)
|
|
1373
|
+
] });
|
|
1374
|
+
break;
|
|
1375
|
+
}
|
|
1376
|
+
case "ended":
|
|
1377
|
+
body = /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: S.answerInChat });
|
|
1378
|
+
actions = /* @__PURE__ */ jsx(
|
|
1379
|
+
"button",
|
|
1380
|
+
{
|
|
1381
|
+
type: "button",
|
|
1382
|
+
"data-avatar-primary": "",
|
|
1383
|
+
"data-boff-explore": "avatar-live-back",
|
|
1384
|
+
className: primaryButton,
|
|
1385
|
+
onClick: onContinueInChat,
|
|
1386
|
+
children: S.backToChat
|
|
1387
|
+
}
|
|
1388
|
+
);
|
|
1389
|
+
break;
|
|
1390
|
+
}
|
|
1391
|
+
return /* @__PURE__ */ jsxs(
|
|
1392
|
+
"div",
|
|
1393
|
+
{
|
|
1394
|
+
ref: rootRef,
|
|
1395
|
+
"data-boff-explore": "avatar-live",
|
|
1396
|
+
"data-phase": phase.name,
|
|
1397
|
+
className: "flex max-h-full w-full flex-col items-center gap-4 overflow-y-auto",
|
|
1398
|
+
children: [
|
|
1399
|
+
top,
|
|
1400
|
+
media,
|
|
1401
|
+
/* @__PURE__ */ jsx(
|
|
1402
|
+
"p",
|
|
1403
|
+
{
|
|
1404
|
+
role: "status",
|
|
1405
|
+
"data-boff-explore": "avatar-live-status",
|
|
1406
|
+
className: outcome ? "text-base font-semibold" : "text-sm text-muted-foreground",
|
|
1407
|
+
children: headline[phase.name]
|
|
1408
|
+
}
|
|
1409
|
+
),
|
|
1410
|
+
body,
|
|
1411
|
+
/* @__PURE__ */ jsx("div", { ref: botBoxRef, "data-boff-explore": "avatar-live-bot-check" }),
|
|
1412
|
+
actions
|
|
1413
|
+
]
|
|
1414
|
+
}
|
|
1415
|
+
);
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
export { AvatarLiveView };
|
|
1419
|
+
//# sourceMappingURL=index.mjs.map
|
|
1420
|
+
//# sourceMappingURL=index.mjs.map
|