@burdenoff/website-sdk 2026.1105.1 → 2026.1105.3
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 +94 -1
- package/dist/avatar-live/index.d.mts +2 -2
- package/dist/avatar-live/index.d.ts +2 -2
- package/dist/avatar-live/index.js +11 -1
- package/dist/avatar-live/index.js.map +1 -1
- package/dist/avatar-live/index.mjs +11 -1
- package/dist/avatar-live/index.mjs.map +1 -1
- package/dist/avatar-live-view/index.d.mts +2 -2
- package/dist/avatar-live-view/index.d.ts +2 -2
- package/dist/avatar-live-view/index.js +915 -103
- package/dist/avatar-live-view/index.js.map +1 -1
- package/dist/avatar-live-view/index.mjs +908 -96
- package/dist/avatar-live-view/index.mjs.map +1 -1
- package/dist/avatar-mic/index.d.mts +187 -62
- package/dist/avatar-mic/index.d.ts +187 -62
- package/dist/avatar-mic/index.js +457 -5
- package/dist/avatar-mic/index.js.map +1 -1
- package/dist/avatar-mic/index.mjs +457 -6
- package/dist/avatar-mic/index.mjs.map +1 -1
- package/dist/{chunk-XIZE37IB.js → chunk-3AIAJ2L4.js} +2 -2
- package/dist/chunk-3AIAJ2L4.js.map +1 -0
- package/dist/{chunk-MVNN6LLO.mjs → chunk-JGU6JW4N.mjs} +2 -2
- package/dist/chunk-JGU6JW4N.mjs.map +1 -0
- package/dist/{explore-avatar-live-config-C-OwXNje.d.ts → explore-avatar-live-config-1zeXdMer.d.ts} +7 -4
- package/dist/{explore-avatar-live-config-OO0hvQCK.d.mts → explore-avatar-live-config-BxiE66Im.d.mts} +7 -4
- package/dist/index.d.mts +6 -5
- package/dist/index.d.ts +6 -5
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/{types-CXqaLlNB.d.mts → types-CTUJNvQu.d.mts} +10 -0
- package/dist/{types-CXqaLlNB.d.ts → types-CTUJNvQu.d.ts} +10 -0
- package/package.json +1 -1
- package/dist/chunk-MVNN6LLO.mjs.map +0 -1
- package/dist/chunk-XIZE37IB.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunk3AIAJ2L4_js = require('../chunk-3AIAJ2L4.js');
|
|
4
4
|
var chunkN7E6Z2GF_js = require('../chunk-N7E6Z2GF.js');
|
|
5
5
|
var chunkC6OJIHNO_js = require('../chunk-C6OJIHNO.js');
|
|
6
6
|
require('../chunk-ZSTFX7FV.js');
|
|
@@ -11,6 +11,8 @@ var jsxRuntime = require('react/jsx-runtime');
|
|
|
11
11
|
// src/components/explore-avatar-api.ts
|
|
12
12
|
var AVAILABILITY_TIMEOUT_MS = 8e3;
|
|
13
13
|
var CREATE_TIMEOUT_MS = 3e4;
|
|
14
|
+
var TURN_TIMEOUT_MS = 8e3;
|
|
15
|
+
var TURN_ANSWER_TIMEOUT_MS = 25e3;
|
|
14
16
|
var MESSAGE_ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
15
17
|
var STT_TOKEN = /^[A-Za-z0-9._~-]{1,4096}$/;
|
|
16
18
|
var CONVERSATION_TOKEN = /^[A-Za-z0-9._~+/=-]{16,512}$/;
|
|
@@ -69,6 +71,17 @@ function readSttOffer(value) {
|
|
|
69
71
|
}
|
|
70
72
|
return { token, expiresAt };
|
|
71
73
|
}
|
|
74
|
+
var MIC_MAX_PRESS_SECONDS = 30;
|
|
75
|
+
var UNREADABLE_MIC = { mode: "unreadable" };
|
|
76
|
+
function readMicOffer(value) {
|
|
77
|
+
if (!isRecord(value)) return UNREADABLE_MIC;
|
|
78
|
+
const keys = Object.keys(value).sort();
|
|
79
|
+
const { mode, maxPressSeconds } = value;
|
|
80
|
+
if (keys.length !== 2 || keys[0] !== "maxPressSeconds" || keys[1] !== "mode" || mode !== "browser" || maxPressSeconds !== MIC_MAX_PRESS_SECONDS) {
|
|
81
|
+
return UNREADABLE_MIC;
|
|
82
|
+
}
|
|
83
|
+
return { mode: "browser", maxPressSeconds: MIC_MAX_PRESS_SECONDS };
|
|
84
|
+
}
|
|
72
85
|
function readAvailability(status, body) {
|
|
73
86
|
if (status !== 200 || !isRecord(body)) return INERT;
|
|
74
87
|
const { available, reason } = body;
|
|
@@ -79,10 +92,11 @@ function readAvailability(status, body) {
|
|
|
79
92
|
const { consentVersion, maxSessionSeconds } = body;
|
|
80
93
|
const botCheck = readBotCheck(body.botCheck);
|
|
81
94
|
const questions = readQuestions(body.questions);
|
|
82
|
-
if (available !== true || typeof consentVersion !== "string" || !
|
|
95
|
+
if (available !== true || typeof consentVersion !== "string" || !chunk3AIAJ2L4_js.isAvatarVersion(consentVersion) || typeof maxSessionSeconds !== "number" || !Number.isInteger(maxSessionSeconds) || maxSessionSeconds < 1 || botCheck === null || questions === null) {
|
|
83
96
|
return INERT;
|
|
84
97
|
}
|
|
85
98
|
const stt = readSttOffer(body.stt);
|
|
99
|
+
const mic = Object.prototype.hasOwnProperty.call(body, "mic") ? readMicOffer(body.mic) : void 0;
|
|
86
100
|
return {
|
|
87
101
|
kind: "available",
|
|
88
102
|
offer: {
|
|
@@ -90,7 +104,8 @@ function readAvailability(status, body) {
|
|
|
90
104
|
maxSessionSeconds,
|
|
91
105
|
botCheck,
|
|
92
106
|
questions,
|
|
93
|
-
...stt === void 0 ? {} : { stt }
|
|
107
|
+
...stt === void 0 ? {} : { stt },
|
|
108
|
+
...mic === void 0 ? {} : { mic }
|
|
94
109
|
}
|
|
95
110
|
};
|
|
96
111
|
}
|
|
@@ -115,6 +130,21 @@ function errorCode(body) {
|
|
|
115
130
|
const { code } = body.error;
|
|
116
131
|
return typeof code === "string" ? code : null;
|
|
117
132
|
}
|
|
133
|
+
var MAX_FOLLOW_UP_ANSWERS = 16;
|
|
134
|
+
var MAX_FOLLOW_UP_SECONDS = 600;
|
|
135
|
+
var FOLLOW_UP_KEYS = ["idleSeconds", "maxAnswerWaitSeconds", "maxAnswers"];
|
|
136
|
+
function isWholeBetween(value, max) {
|
|
137
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 1 && value <= max;
|
|
138
|
+
}
|
|
139
|
+
function readFollowUps(value) {
|
|
140
|
+
if (!isRecord(value)) return void 0;
|
|
141
|
+
const keys = Object.keys(value).sort();
|
|
142
|
+
const { maxAnswers, idleSeconds, maxAnswerWaitSeconds } = value;
|
|
143
|
+
if (keys.length !== FOLLOW_UP_KEYS.length || keys.some((key, at) => key !== FOLLOW_UP_KEYS[at]) || !isWholeBetween(maxAnswers, MAX_FOLLOW_UP_ANSWERS) || !isWholeBetween(idleSeconds, MAX_FOLLOW_UP_SECONDS) || !isWholeBetween(maxAnswerWaitSeconds, MAX_FOLLOW_UP_SECONDS)) {
|
|
144
|
+
return void 0;
|
|
145
|
+
}
|
|
146
|
+
return { maxAnswers, idleSeconds, maxAnswerWaitSeconds };
|
|
147
|
+
}
|
|
118
148
|
function readCreated(body) {
|
|
119
149
|
if (!isRecord(body)) return FAILED;
|
|
120
150
|
const { sessionId, endSecret } = body;
|
|
@@ -132,6 +162,7 @@ function readCreated(body) {
|
|
|
132
162
|
if (typeof livekitUrl !== "string" || !livekitUrl.startsWith("wss://") || typeof participantToken !== "string" || participantToken === "" || typeof workerIdentity !== "string" || workerIdentity === "" || typeof locale !== "string" || typeof text !== "string") {
|
|
133
163
|
return unusable;
|
|
134
164
|
}
|
|
165
|
+
const followUps = readFollowUps(body.followUps);
|
|
135
166
|
return {
|
|
136
167
|
kind: "created",
|
|
137
168
|
session: {
|
|
@@ -140,7 +171,8 @@ function readCreated(body) {
|
|
|
140
171
|
endsAt,
|
|
141
172
|
join: { livekitUrl, participantToken, workerIdentity },
|
|
142
173
|
captions: { locale, text },
|
|
143
|
-
excerpt: body.excerpt === true
|
|
174
|
+
excerpt: body.excerpt === true,
|
|
175
|
+
...followUps === void 0 ? {} : { followUps }
|
|
144
176
|
}
|
|
145
177
|
};
|
|
146
178
|
}
|
|
@@ -186,7 +218,7 @@ async function requestAvailability(target) {
|
|
|
186
218
|
}
|
|
187
219
|
}
|
|
188
220
|
async function requestAvailabilityAnswer(target) {
|
|
189
|
-
if (!
|
|
221
|
+
if (!chunk3AIAJ2L4_js.isAvatarSite(target.site) || !LOCALE.test(target.locale)) return INERT;
|
|
190
222
|
const query = new URLSearchParams({
|
|
191
223
|
site: target.site,
|
|
192
224
|
locale: target.locale
|
|
@@ -271,6 +303,96 @@ function sendEnd(target) {
|
|
|
271
303
|
} catch {
|
|
272
304
|
}
|
|
273
305
|
}
|
|
306
|
+
var TURN_REFUSALS = /* @__PURE__ */ new Map([
|
|
307
|
+
["no_time", "no_time"],
|
|
308
|
+
["max_answers", "max_answers"],
|
|
309
|
+
["not_eligible", "not_eligible"],
|
|
310
|
+
["answer_claimed", "answer_claimed"],
|
|
311
|
+
["limits", "limits"],
|
|
312
|
+
["paused", "paused"],
|
|
313
|
+
["busy", "busy"]
|
|
314
|
+
]);
|
|
315
|
+
var MAX_TURN_RETRY_AFTER_SECONDS = 600;
|
|
316
|
+
function retryAfterOf(body) {
|
|
317
|
+
if (!isRecord(body) || !isRecord(body.error)) return void 0;
|
|
318
|
+
const { retryAfterSeconds } = body.error;
|
|
319
|
+
return isWholeBetween(retryAfterSeconds, MAX_TURN_RETRY_AFTER_SECONDS) ? retryAfterSeconds : void 0;
|
|
320
|
+
}
|
|
321
|
+
var TURN_KEY = /^[A-Za-z0-9_-]{8,64}$/;
|
|
322
|
+
var LOST = { kind: "failed", lost: true };
|
|
323
|
+
var TURN_FAILED = { kind: "failed", lost: false };
|
|
324
|
+
function readAccepted(body) {
|
|
325
|
+
if (!isRecord(body) || body.accepted !== true || !isRecord(body.captions)) {
|
|
326
|
+
return LOST;
|
|
327
|
+
}
|
|
328
|
+
const { locale, text } = body.captions;
|
|
329
|
+
if (typeof locale !== "string" || typeof text !== "string") return LOST;
|
|
330
|
+
return {
|
|
331
|
+
kind: "accepted",
|
|
332
|
+
captions: { locale, text },
|
|
333
|
+
excerpt: body.excerpt === true
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
function readTurn(kind, status, body) {
|
|
337
|
+
if (status === 200) {
|
|
338
|
+
if (kind === "answer") return readAccepted(body);
|
|
339
|
+
return isRecord(body) && body.ok === true ? { kind: "ok" } : LOST;
|
|
340
|
+
}
|
|
341
|
+
const code = errorCode(body);
|
|
342
|
+
const refusal = code === null ? void 0 : TURN_REFUSALS.get(code);
|
|
343
|
+
if (refusal !== void 0) {
|
|
344
|
+
const retryAfterSeconds = retryAfterOf(body);
|
|
345
|
+
return retryAfterSeconds === void 0 ? { kind: "refused", reason: refusal } : { kind: "refused", reason: refusal, retryAfterSeconds };
|
|
346
|
+
}
|
|
347
|
+
if (status === 404) return { kind: "gone" };
|
|
348
|
+
return status >= 500 ? LOST : TURN_FAILED;
|
|
349
|
+
}
|
|
350
|
+
function requestTurn(target) {
|
|
351
|
+
const { kind, messageId, idempotencyKey } = target;
|
|
352
|
+
const nonce = newIdempotencyKey();
|
|
353
|
+
if (!SESSION_ID.test(target.sessionId) || !END_SECRET.test(target.secret) || nonce === null || (kind === "answer" ? messageId === void 0 || !MESSAGE_ID.test(messageId) : messageId !== void 0 || idempotencyKey !== void 0) || idempotencyKey !== void 0 && !TURN_KEY.test(idempotencyKey)) {
|
|
354
|
+
return Promise.resolve(TURN_FAILED);
|
|
355
|
+
}
|
|
356
|
+
const payload = JSON.stringify({
|
|
357
|
+
secret: target.secret,
|
|
358
|
+
kind,
|
|
359
|
+
nonce,
|
|
360
|
+
...messageId === void 0 ? {} : { messageId },
|
|
361
|
+
...idempotencyKey === void 0 ? {} : { idempotencyKey }
|
|
362
|
+
});
|
|
363
|
+
const controller = new AbortController();
|
|
364
|
+
const answer = (async () => {
|
|
365
|
+
try {
|
|
366
|
+
const response = await fetch(
|
|
367
|
+
`${target.base}/sessions/${encodeURIComponent(target.sessionId)}/turn`,
|
|
368
|
+
{
|
|
369
|
+
method: "POST",
|
|
370
|
+
credentials: "omit",
|
|
371
|
+
cache: "no-store",
|
|
372
|
+
headers: { ...target.headers },
|
|
373
|
+
body: payload,
|
|
374
|
+
signal: controller.signal
|
|
375
|
+
}
|
|
376
|
+
);
|
|
377
|
+
return readTurn(kind, response.status, await readJson(response));
|
|
378
|
+
} catch {
|
|
379
|
+
return LOST;
|
|
380
|
+
}
|
|
381
|
+
})();
|
|
382
|
+
return new Promise((resolve) => {
|
|
383
|
+
const timer = setTimeout(
|
|
384
|
+
() => {
|
|
385
|
+
controller.abort();
|
|
386
|
+
resolve(LOST);
|
|
387
|
+
},
|
|
388
|
+
kind === "answer" ? TURN_ANSWER_TIMEOUT_MS : TURN_TIMEOUT_MS
|
|
389
|
+
);
|
|
390
|
+
void answer.then((result) => {
|
|
391
|
+
clearTimeout(timer);
|
|
392
|
+
resolve(result);
|
|
393
|
+
});
|
|
394
|
+
});
|
|
395
|
+
}
|
|
274
396
|
var BOT_BINDING_PREFIX = "boff-web-avatar-cdata/v1";
|
|
275
397
|
function webCryptoSubtle() {
|
|
276
398
|
const subtle = globalThis.crypto?.subtle;
|
|
@@ -368,7 +490,14 @@ var EN = {
|
|
|
368
490
|
usedToday: "You've had today's avatar session. Answers will show here as text.",
|
|
369
491
|
needsAnswerAny: "Ask a question here, and the avatar reads the answer aloud.",
|
|
370
492
|
startNewChatDetailAny: "The avatar reads aloud only the first answer in a new chat started in the Avatar tab, provided it passes the speech checks.",
|
|
371
|
-
notEligibleAny: "The avatar can't read this answer aloud. The answer and sources are in Chat."
|
|
493
|
+
notEligibleAny: "The avatar can't read this answer aloud. The answer and sources are in Chat.",
|
|
494
|
+
followUpIdle: "The avatar ends after about thirty seconds without a question.",
|
|
495
|
+
followUpNoTime: "There isn't enough time left in this session to read that answer. It's in Chat.",
|
|
496
|
+
followUpMaxAnswers: "The avatar has read all the answers it can in this session. This one is in Chat.",
|
|
497
|
+
followUpLast: "That was the last answer the avatar can read in this session.",
|
|
498
|
+
followUpLimits: "The live avatar has reached its limit for now. This answer is in Chat.",
|
|
499
|
+
followUpPaused: "The live avatar is paused right now. This answer is in Chat.",
|
|
500
|
+
followUpUnread: "The avatar couldn't read that answer. It's in Chat."
|
|
372
501
|
};
|
|
373
502
|
var ZH = {
|
|
374
503
|
checking: "\u6B63\u5728\u68C0\u67E5\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u662F\u5426\u53EF\u7528\u2026",
|
|
@@ -429,7 +558,16 @@ var ZH = {
|
|
|
429
558
|
usedToday: "\u60A8\u4ECA\u5929\u5DF2\u4F7F\u7528\u8FC7\u865A\u62DF\u5F62\u8C61\u3002\u56DE\u7B54\u5C06\u4EE5\u6587\u5B57\u663E\u793A\u5728\u8FD9\u91CC\u3002",
|
|
430
559
|
needsAnswerAny: "\u5728\u8FD9\u91CC\u63D0\u95EE\uFF0C\u865A\u62DF\u5F62\u8C61\u4F1A\u6717\u8BFB\u56DE\u7B54\u3002",
|
|
431
560
|
startNewChatDetailAny: "\u865A\u62DF\u5F62\u8C61\u53EA\u6717\u8BFB\u5728\u201C\u865A\u62DF\u5F62\u8C61\u201D\u6807\u7B7E\u9875\u4E2D\u5F00\u59CB\u7684\u65B0\u804A\u5929\u7684\u7B2C\u4E00\u4E2A\u56DE\u7B54\uFF0C\u4E14\u8BE5\u56DE\u7B54\u987B\u901A\u8FC7\u8BED\u97F3\u68C0\u67E5\u3002",
|
|
432
|
-
notEligibleAny: "\u865A\u62DF\u5F62\u8C61\u65E0\u6CD5\u6717\u8BFB\u6B64\u56DE\u7B54\u3002\u56DE\u7B54\u548C\u6765\u6E90\u5728\u804A\u5929\u4E2D\u3002"
|
|
561
|
+
notEligibleAny: "\u865A\u62DF\u5F62\u8C61\u65E0\u6CD5\u6717\u8BFB\u6B64\u56DE\u7B54\u3002\u56DE\u7B54\u548C\u6765\u6E90\u5728\u804A\u5929\u4E2D\u3002",
|
|
562
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft, the lines of a session that reads the answers after the first
|
|
563
|
+
// (Stage D).
|
|
564
|
+
followUpIdle: "\u5982\u679C\u7EA6\u4E09\u5341\u79D2\u5185\u6CA1\u6709\u65B0\u95EE\u9898\uFF0C\u865A\u62DF\u5F62\u8C61\u5C31\u4F1A\u7ED3\u675F\u3002",
|
|
565
|
+
followUpNoTime: "\u672C\u6B21\u4F1A\u8BDD\u5269\u4F59\u65F6\u95F4\u4E0D\u8DB3\uFF0C\u65E0\u6CD5\u6717\u8BFB\u8BE5\u56DE\u7B54\u3002\u56DE\u7B54\u5728\u804A\u5929\u4E2D\u3002",
|
|
566
|
+
followUpMaxAnswers: "\u865A\u62DF\u5F62\u8C61\u5DF2\u6717\u8BFB\u672C\u6B21\u4F1A\u8BDD\u53EF\u6717\u8BFB\u7684\u5168\u90E8\u56DE\u7B54\u3002\u6B64\u56DE\u7B54\u5728\u804A\u5929\u4E2D\u3002",
|
|
567
|
+
followUpLast: "\u8FD9\u662F\u865A\u62DF\u5F62\u8C61\u5728\u672C\u6B21\u4F1A\u8BDD\u4E2D\u53EF\u6717\u8BFB\u7684\u6700\u540E\u4E00\u4E2A\u56DE\u7B54\u3002",
|
|
568
|
+
followUpLimits: "\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u6682\u65F6\u5DF2\u8FBE\u5230\u4F7F\u7528\u4E0A\u9650\u3002\u6B64\u56DE\u7B54\u5728\u804A\u5929\u4E2D\u3002",
|
|
569
|
+
followUpPaused: "\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u76EE\u524D\u5DF2\u6682\u505C\u3002\u6B64\u56DE\u7B54\u5728\u804A\u5929\u4E2D\u3002",
|
|
570
|
+
followUpUnread: "\u865A\u62DF\u5F62\u8C61\u65E0\u6CD5\u6717\u8BFB\u8BE5\u56DE\u7B54\u3002\u56DE\u7B54\u5728\u804A\u5929\u4E2D\u3002"
|
|
433
571
|
};
|
|
434
572
|
var HI = {
|
|
435
573
|
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",
|
|
@@ -490,7 +628,16 @@ var HI = {
|
|
|
490
628
|
usedToday: "\u0906\u091C \u0915\u093E \u0905\u0935\u0924\u093E\u0930 \u0938\u0924\u094D\u0930 \u0906\u092A \u0932\u0947 \u091A\u0941\u0915\u0947 \u0939\u0948\u0902\u0964 \u0909\u0924\u094D\u0924\u0930 \u092F\u0939\u093E\u0901 \u091F\u0947\u0915\u094D\u0938\u094D\u091F \u0915\u0947 \u0930\u0942\u092A \u092E\u0947\u0902 \u0926\u093F\u0916\u0947\u0902\u0917\u0947\u0964",
|
|
491
629
|
needsAnswerAny: "\u092F\u0939\u093E\u0901 \u092A\u094D\u0930\u0936\u094D\u0928 \u092A\u0942\u091B\u0947\u0902, \u0914\u0930 \u0905\u0935\u0924\u093E\u0930 \u0909\u0924\u094D\u0924\u0930 \u091C\u093C\u094B\u0930 \u0938\u0947 \u092A\u0922\u093C\u0947\u0917\u093E\u0964",
|
|
492
630
|
startNewChatDetailAny: "\u0905\u0935\u0924\u093E\u0930 \u0915\u0947\u0935\u0932 \u0905\u0935\u0924\u093E\u0930 \u091F\u0948\u092C \u092E\u0947\u0902 \u0936\u0941\u0930\u0942 \u0915\u0940 \u0917\u0908 \u0928\u0908 \u091A\u0948\u091F \u0915\u093E \u092A\u0939\u0932\u093E \u0909\u0924\u094D\u0924\u0930 \u091C\u093C\u094B\u0930 \u0938\u0947 \u092A\u0922\u093C\u0924\u093E \u0939\u0948, \u092C\u0936\u0930\u094D\u0924\u0947 \u0935\u0939 \u0935\u093E\u0923\u0940 \u091C\u093E\u0901\u091A \u092E\u0947\u0902 \u092A\u093E\u0938 \u0939\u094B\u0964",
|
|
493
|
-
notEligibleAny: "\u0905\u0935\u0924\u093E\u0930 \u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u091C\u093C\u094B\u0930 \u0938\u0947 \u0928\u0939\u0940\u0902 \u092A\u0922\u093C \u0938\u0915\u0924\u093E\u0964 \u0909\u0924\u094D\u0924\u0930 \u0914\u0930 \u0938\u094D\u0930\u094B\u0924 \u091A\u0948\u091F \u092E\u0947\u0902 \u0939\u0948\u0902\u0964"
|
|
631
|
+
notEligibleAny: "\u0905\u0935\u0924\u093E\u0930 \u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u091C\u093C\u094B\u0930 \u0938\u0947 \u0928\u0939\u0940\u0902 \u092A\u0922\u093C \u0938\u0915\u0924\u093E\u0964 \u0909\u0924\u094D\u0924\u0930 \u0914\u0930 \u0938\u094D\u0930\u094B\u0924 \u091A\u0948\u091F \u092E\u0947\u0902 \u0939\u0948\u0902\u0964",
|
|
632
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft, the lines of a session that reads the answers after the first
|
|
633
|
+
// (Stage D).
|
|
634
|
+
followUpIdle: "\u092C\u093F\u0928\u093E \u0915\u093F\u0938\u0940 \u092A\u094D\u0930\u0936\u094D\u0928 \u0915\u0947 \u0932\u0917\u092D\u0917 \u0924\u0940\u0938 \u0938\u0947\u0915\u0902\u0921 \u092C\u093E\u0926 \u0905\u0935\u0924\u093E\u0930 \u0938\u092E\u093E\u092A\u094D\u0924 \u0939\u094B \u091C\u093E\u0924\u093E \u0939\u0948\u0964",
|
|
635
|
+
followUpNoTime: "\u0907\u0938 \u0938\u0924\u094D\u0930 \u092E\u0947\u0902 \u0935\u0939 \u0909\u0924\u094D\u0924\u0930 \u092A\u0922\u093C\u0928\u0947 \u0915\u0947 \u0932\u093F\u090F \u092A\u0930\u094D\u092F\u093E\u092A\u094D\u0924 \u0938\u092E\u092F \u0928\u0939\u0940\u0902 \u092C\u091A\u093E \u0939\u0948\u0964 \u0909\u0924\u094D\u0924\u0930 \u091A\u0948\u091F \u092E\u0947\u0902 \u0939\u0948\u0964",
|
|
636
|
+
followUpMaxAnswers: "\u0905\u0935\u0924\u093E\u0930 \u0907\u0938 \u0938\u0924\u094D\u0930 \u092E\u0947\u0902 \u091C\u093F\u0924\u0928\u0947 \u0909\u0924\u094D\u0924\u0930 \u092A\u0922\u093C \u0938\u0915\u0924\u093E \u0939\u0948, \u0938\u092C \u092A\u0922\u093C \u091A\u0941\u0915\u093E \u0939\u0948\u0964 \u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u091A\u0948\u091F \u092E\u0947\u0902 \u0939\u0948\u0964",
|
|
637
|
+
followUpLast: "\u092F\u0939 \u0907\u0938 \u0938\u0924\u094D\u0930 \u092E\u0947\u0902 \u0905\u0935\u0924\u093E\u0930 \u0926\u094D\u0935\u093E\u0930\u093E \u092A\u0922\u093C\u093E \u091C\u093E \u0938\u0915\u0928\u0947 \u0935\u093E\u0932\u093E \u0905\u0902\u0924\u093F\u092E \u0909\u0924\u094D\u0924\u0930 \u0925\u093E\u0964",
|
|
638
|
+
followUpLimits: "\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 \u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u091A\u0948\u091F \u092E\u0947\u0902 \u0939\u0948\u0964",
|
|
639
|
+
followUpPaused: "\u0932\u093E\u0907\u0935 \u0905\u0935\u0924\u093E\u0930 \u0905\u092D\u0940 \u0930\u0941\u0915\u093E \u0939\u0941\u0906 \u0939\u0948\u0964 \u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u091A\u0948\u091F \u092E\u0947\u0902 \u0939\u0948\u0964",
|
|
640
|
+
followUpUnread: "\u0905\u0935\u0924\u093E\u0930 \u0935\u0939 \u0909\u0924\u094D\u0924\u0930 \u0928\u0939\u0940\u0902 \u092A\u0922\u093C \u0938\u0915\u093E\u0964 \u0909\u0924\u094D\u0924\u0930 \u091A\u0948\u091F \u092E\u0947\u0902 \u0939\u0948\u0964"
|
|
494
641
|
};
|
|
495
642
|
var ES = {
|
|
496
643
|
checking: "Comprobando si el avatar en vivo est\xE1 disponible\u2026",
|
|
@@ -551,7 +698,16 @@ var ES = {
|
|
|
551
698
|
usedToday: "Ya has usado la sesi\xF3n de avatar de hoy. Las respuestas se mostrar\xE1n aqu\xED como texto.",
|
|
552
699
|
needsAnswerAny: "Haz una pregunta aqu\xED y el avatar leer\xE1 la respuesta en voz alta.",
|
|
553
700
|
startNewChatDetailAny: "El avatar solo lee en voz alta la primera respuesta de un chat nuevo iniciado en la pesta\xF1a Avatar, si supera las comprobaciones de voz.",
|
|
554
|
-
notEligibleAny: "El avatar no puede leer esta respuesta en voz alta. La respuesta y las fuentes est\xE1n en el chat."
|
|
701
|
+
notEligibleAny: "El avatar no puede leer esta respuesta en voz alta. La respuesta y las fuentes est\xE1n en el chat.",
|
|
702
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft, the lines of a session that reads the answers after the first
|
|
703
|
+
// (Stage D).
|
|
704
|
+
followUpIdle: "El avatar termina tras unos treinta segundos sin preguntas.",
|
|
705
|
+
followUpNoTime: "No queda tiempo suficiente en esta sesi\xF3n para leer esa respuesta. Est\xE1 en el chat.",
|
|
706
|
+
followUpMaxAnswers: "El avatar ya ha le\xEDdo todas las respuestas que puede en esta sesi\xF3n. Esta est\xE1 en el chat.",
|
|
707
|
+
followUpLast: "Esa fue la \xFAltima respuesta que el avatar puede leer en esta sesi\xF3n.",
|
|
708
|
+
followUpLimits: "El avatar en vivo ha alcanzado su l\xEDmite por ahora. Esta respuesta est\xE1 en el chat.",
|
|
709
|
+
followUpPaused: "El avatar en vivo est\xE1 en pausa ahora mismo. Esta respuesta est\xE1 en el chat.",
|
|
710
|
+
followUpUnread: "El avatar no pudo leer esa respuesta. Est\xE1 en el chat."
|
|
555
711
|
};
|
|
556
712
|
var AR = {
|
|
557
713
|
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",
|
|
@@ -612,7 +768,16 @@ var AR = {
|
|
|
612
768
|
usedToday: "\u0644\u0642\u062F \u0627\u0633\u062A\u062E\u062F\u0645\u062A \u062C\u0644\u0633\u0629 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0644\u0647\u0630\u0627 \u0627\u0644\u064A\u0648\u0645. \u0633\u062A\u0638\u0647\u0631 \u0627\u0644\u0625\u062C\u0627\u0628\u0627\u062A \u0647\u0646\u0627 \u0646\u0635\u064B\u0627.",
|
|
613
769
|
needsAnswerAny: "\u0627\u0637\u0631\u062D \u0633\u0624\u0627\u0644\u064B\u0627 \u0647\u0646\u0627\u060C \u0648\u0633\u064A\u0642\u0631\u0623 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0628\u0635\u0648\u062A \u0639\u0627\u0644\u064D.",
|
|
614
770
|
startNewChatDetailAny: "\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\u0642\u0637 \u0641\u064A \u062F\u0631\u062F\u0634\u0629 \u062C\u062F\u064A\u062F\u0629 \u0628\u062F\u0623\u062A \u0641\u064A \u0639\u0644\u0627\u0645\u0629 \u062A\u0628\u0648\u064A\u0628 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631\u060C \u0628\u0634\u0631\u0637 \u0623\u0646 \u062A\u062C\u062A\u0627\u0632 \u0641\u062D\u0648\u0635 \u0627\u0644\u0646\u0637\u0642.",
|
|
615
|
-
notEligibleAny: "\u0644\u0627 \u064A\u0645\u0643\u0646 \u0644\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0642\u0631\u0627\u0621\u0629 \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0628\u0635\u0648\u062A \u0639\u0627\u0644\u064D. \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0648\u0627\u0644\u0645\u0635\u0627\u062F\u0631 \u0645\u0648\u062C\u0648\u062F\u0629 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629."
|
|
771
|
+
notEligibleAny: "\u0644\u0627 \u064A\u0645\u0643\u0646 \u0644\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0642\u0631\u0627\u0621\u0629 \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0628\u0635\u0648\u062A \u0639\u0627\u0644\u064D. \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0648\u0627\u0644\u0645\u0635\u0627\u062F\u0631 \u0645\u0648\u062C\u0648\u062F\u0629 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629.",
|
|
772
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft, the lines of a session that reads the answers after the first
|
|
773
|
+
// (Stage D).
|
|
774
|
+
followUpIdle: "\u064A\u0646\u062A\u0647\u064A \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0628\u0639\u062F \u0646\u062D\u0648 \u062B\u0644\u0627\u062B\u064A\u0646 \u062B\u0627\u0646\u064A\u0629 \u0645\u0646 \u062F\u0648\u0646 \u0633\u0624\u0627\u0644.",
|
|
775
|
+
followUpNoTime: "\u0644\u0645 \u064A\u062A\u0628\u0642\u064E\u0651 \u0648\u0642\u062A \u0643\u0627\u0641\u064D \u0641\u064A \u0647\u0630\u0647 \u0627\u0644\u062C\u0644\u0633\u0629 \u0644\u0642\u0631\u0627\u0621\u0629 \u062A\u0644\u0643 \u0627\u0644\u0625\u062C\u0627\u0628\u0629. \u0625\u0646\u0647\u0627 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629.",
|
|
776
|
+
followUpMaxAnswers: "\u0642\u0631\u0623 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0643\u0644 \u0627\u0644\u0625\u062C\u0627\u0628\u0627\u062A \u0627\u0644\u062A\u064A \u064A\u0645\u0643\u0646\u0647 \u0642\u0631\u0627\u0621\u062A\u0647\u0627 \u0641\u064A \u0647\u0630\u0647 \u0627\u0644\u062C\u0644\u0633\u0629. \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629.",
|
|
777
|
+
followUpLast: "\u0643\u0627\u0646\u062A \u062A\u0644\u0643 \u0622\u062E\u0631 \u0625\u062C\u0627\u0628\u0629 \u064A\u0645\u0643\u0646 \u0644\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0642\u0631\u0627\u0621\u062A\u0647\u0627 \u0641\u064A \u0647\u0630\u0647 \u0627\u0644\u062C\u0644\u0633\u0629.",
|
|
778
|
+
followUpLimits: "\u0628\u0644\u063A \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0645\u0628\u0627\u0634\u0631 \u062D\u062F\u0647 \u0627\u0644\u0622\u0646. \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629.",
|
|
779
|
+
followUpPaused: "\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. \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629.",
|
|
780
|
+
followUpUnread: "\u062A\u0639\u0630\u0651\u0631 \u0639\u0644\u0649 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0642\u0631\u0627\u0621\u0629 \u062A\u0644\u0643 \u0627\u0644\u0625\u062C\u0627\u0628\u0629. \u0625\u0646\u0647\u0627 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629."
|
|
616
781
|
};
|
|
617
782
|
var TA = {
|
|
618
783
|
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",
|
|
@@ -673,7 +838,16 @@ var TA = {
|
|
|
673
838
|
usedToday: "\u0B87\u0BA9\u0BCD\u0BB1\u0BC8\u0BAF \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0B85\u0BAE\u0BB0\u0BCD\u0BB5\u0BC8 \u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BAA\u0BAF\u0BA9\u0BCD\u0BAA\u0B9F\u0BC1\u0BA4\u0BCD\u0BA4\u0BBF\u0BB5\u0BBF\u0B9F\u0BCD\u0B9F\u0BC0\u0BB0\u0BCD\u0B95\u0BB3\u0BCD. \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD\u0B95\u0BB3\u0BCD \u0B87\u0B99\u0BCD\u0B95\u0BC7 \u0B89\u0BB0\u0BC8\u0BAF\u0BBE\u0B95\u0B95\u0BCD \u0B95\u0BBE\u0B9F\u0BCD\u0B9F\u0BAA\u0BCD\u0BAA\u0B9F\u0BC1\u0BAE\u0BCD.",
|
|
674
839
|
needsAnswerAny: "\u0B87\u0B99\u0BCD\u0B95\u0BC7 \u0B92\u0BB0\u0BC1 \u0B95\u0BC7\u0BB3\u0BCD\u0BB5\u0BBF\u0BAF\u0BC8\u0B95\u0BCD \u0B95\u0BC7\u0BB3\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD, \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0B89\u0BB0\u0B95\u0BCD\u0B95\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95\u0BC1\u0BAE\u0BCD.",
|
|
675
840
|
startNewChatDetailAny: "\u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BA4\u0BBE\u0BB5\u0BB2\u0BBF\u0BB2\u0BCD \u0BA4\u0BCA\u0B9F\u0B99\u0BCD\u0B95\u0BBF\u0BAF \u0BAA\u0BC1\u0BA4\u0BBF\u0BAF \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 \u0BAA\u0BC7\u0B9A\u0BCD\u0B9A\u0BC1\u0B9A\u0BCD \u0B9A\u0BB0\u0BBF\u0BAA\u0BBE\u0BB0\u0BCD\u0BAA\u0BCD\u0BAA\u0BC1\u0B95\u0BB3\u0BBF\u0BB2\u0BCD \u0BA4\u0BC7\u0BB1\u0BBF\u0BA9\u0BBE\u0BB2\u0BCD, \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0B89\u0BB0\u0B95\u0BCD\u0B95\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95\u0BC1\u0BAE\u0BCD.",
|
|
676
|
-
notEligibleAny: "\u0B87\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BBE\u0BB2\u0BCD \u0B89\u0BB0\u0B95\u0BCD\u0B95\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95 \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BBE\u0BA4\u0BC1. \u0BAA\u0BA4\u0BBF\u0BB2\u0BC1\u0BAE\u0BCD \u0B86\u0BA4\u0BBE\u0BB0\u0B99\u0BCD\u0B95\u0BB3\u0BC1\u0BAE\u0BCD \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA9."
|
|
841
|
+
notEligibleAny: "\u0B87\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BBE\u0BB2\u0BCD \u0B89\u0BB0\u0B95\u0BCD\u0B95\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95 \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BBE\u0BA4\u0BC1. \u0BAA\u0BA4\u0BBF\u0BB2\u0BC1\u0BAE\u0BCD \u0B86\u0BA4\u0BBE\u0BB0\u0B99\u0BCD\u0B95\u0BB3\u0BC1\u0BAE\u0BCD \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA9.",
|
|
842
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft, the lines of a session that reads the answers after the first
|
|
843
|
+
// (Stage D).
|
|
844
|
+
followUpIdle: "\u0B95\u0BC7\u0BB3\u0BCD\u0BB5\u0BBF \u0B87\u0BB2\u0BCD\u0BB2\u0BBE\u0BAE\u0BB2\u0BCD \u0B9A\u0BC1\u0BAE\u0BBE\u0BB0\u0BCD \u0BAE\u0BC1\u0BAA\u0BCD\u0BAA\u0BA4\u0BC1 \u0BB5\u0BBF\u0BA9\u0BBE\u0B9F\u0BBF\u0B95\u0BB3\u0BCD \u0B95\u0BB4\u0BBF\u0BA4\u0BCD\u0BA4\u0BC1 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0B9F\u0BC8\u0BAF\u0BC1\u0BAE\u0BCD.",
|
|
845
|
+
followUpNoTime: "\u0B85\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BC8\u0BAA\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95 \u0B87\u0BA8\u0BCD\u0BA4 \u0B85\u0BAE\u0BB0\u0BCD\u0BB5\u0BBF\u0BB2\u0BCD \u0BAA\u0BCB\u0BA4\u0BC1\u0BAE\u0BBE\u0BA9 \u0BA8\u0BC7\u0BB0\u0BAE\u0BCD \u0B87\u0BB2\u0BCD\u0BB2\u0BC8. \u0B85\u0BA4\u0BC1 \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1.",
|
|
846
|
+
followUpMaxAnswers: "\u0B87\u0BA8\u0BCD\u0BA4 \u0B85\u0BAE\u0BB0\u0BCD\u0BB5\u0BBF\u0BB2\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95\u0B95\u0BCD\u0B95\u0BC2\u0B9F\u0BBF\u0BAF \u0B8E\u0BB2\u0BCD\u0BB2\u0BBE\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD\u0B95\u0BB3\u0BC8\u0BAF\u0BC1\u0BAE\u0BCD \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BAA\u0B9F\u0BBF\u0BA4\u0BCD\u0BA4\u0BC1\u0BB5\u0BBF\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1. \u0B87\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1.",
|
|
847
|
+
followUpLast: "\u0B87\u0BA8\u0BCD\u0BA4 \u0B85\u0BAE\u0BB0\u0BCD\u0BB5\u0BBF\u0BB2\u0BCD \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95\u0B95\u0BCD\u0B95\u0BC2\u0B9F\u0BBF\u0BAF \u0B95\u0B9F\u0BC8\u0B9A\u0BBF \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0B85\u0BA4\u0BC1\u0BA4\u0BBE\u0BA9\u0BCD.",
|
|
848
|
+
followUpLimits: "\u0BA8\u0BC7\u0BB0\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BA4\u0BB1\u0BCD\u0BAA\u0BCB\u0BA4\u0BC1 \u0B85\u0BA4\u0BA9\u0BCD \u0BB5\u0BB0\u0BAE\u0BCD\u0BAA\u0BC8 \u0B8E\u0B9F\u0BCD\u0B9F\u0BBF\u0BB5\u0BBF\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1. \u0B87\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1.",
|
|
849
|
+
followUpPaused: "\u0BA8\u0BC7\u0BB0\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BA4\u0BB1\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. \u0B87\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1.",
|
|
850
|
+
followUpUnread: "\u0B85\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BBE\u0BB2\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95 \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8. \u0B85\u0BA4\u0BC1 \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1."
|
|
677
851
|
};
|
|
678
852
|
var STRINGS = {
|
|
679
853
|
en: EN,
|
|
@@ -686,6 +860,7 @@ var STRINGS = {
|
|
|
686
860
|
function avatarLiveStrings(locale) {
|
|
687
861
|
return STRINGS[locale] ?? EN;
|
|
688
862
|
}
|
|
863
|
+
var BROWSER_MIC_NOTICE_VERSION = "web-mic-2026-11-21-c";
|
|
689
864
|
var CONSENT = /* @__PURE__ */ new Map([
|
|
690
865
|
[
|
|
691
866
|
"web-consent-2026-11",
|
|
@@ -823,10 +998,95 @@ var CONSENT = /* @__PURE__ */ new Map([
|
|
|
823
998
|
questions: "any"
|
|
824
999
|
}
|
|
825
1000
|
}
|
|
1001
|
+
],
|
|
1002
|
+
[
|
|
1003
|
+
// Stage D (BOFF-9797), a DRAFT for the operator's review that no environment uses yet (global-speech-svc reserves the
|
|
1004
|
+
// same two versions): web-consent-2026-11-12-b with only its intro changed. The avatar reads the answer to each
|
|
1005
|
+
// question asked in the Avatar tab, in one session, waits about thirty seconds after an answer for the next question,
|
|
1006
|
+
// then ends; the session lasts at most three minutes, and an answer that does not fit the time left is read in part
|
|
1007
|
+
// (and said so) or not read, and stays in Chat. The notice files of record and their digests are a gitops change of
|
|
1008
|
+
// their own, made with the review.
|
|
1009
|
+
"web-consent-2026-11-19-d",
|
|
1010
|
+
{
|
|
1011
|
+
en: {
|
|
1012
|
+
title: "Live AI avatar (beta)",
|
|
1013
|
+
introProduct: null,
|
|
1014
|
+
intro: "This is an AI avatar, not a real person. It reads aloud the answers to the questions you ask in the Avatar tab, provided each passes the speech checks. After an answer, it waits about thirty seconds for your next question, then ends. A session lasts at most three minutes; if an answer doesn't fit in the time left, it reads only the first part and says so, or doesn't read it, and the full answer stays in Chat. The answers are AI-generated and can be wrong. Don't put personal details in a question you type.",
|
|
1015
|
+
processing: "When you start, Cloudflare checks for bots. The answer text goes to Google to create the voice. Our server sends that voice through LiveKit to Protoface, which makes the avatar video streamed through LiveKit. Your browser connects to LiveKit, which sees your IP address.",
|
|
1016
|
+
storage: "Your microphone and camera are not used. We do not record the live session or store the generated voice. We keep a record of this consent. The privacy notice explains our safeguards, storage periods and the providers' processing. For people aged eighteen and over.",
|
|
1017
|
+
privacyLink: "How we handle this",
|
|
1018
|
+
botCheck: "turnstile",
|
|
1019
|
+
questions: "any",
|
|
1020
|
+
followUps: true
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
],
|
|
1024
|
+
[
|
|
1025
|
+
// The words of web-consent-2026-11-19-d without "When you start, Cloudflare checks for bots.", for a server that runs
|
|
1026
|
+
// no bot check. A DRAFT, like it.
|
|
1027
|
+
"web-consent-2026-11-20-d",
|
|
1028
|
+
{
|
|
1029
|
+
en: {
|
|
1030
|
+
title: "Live AI avatar (beta)",
|
|
1031
|
+
introProduct: null,
|
|
1032
|
+
intro: "This is an AI avatar, not a real person. It reads aloud the answers to the questions you ask in the Avatar tab, provided each passes the speech checks. After an answer, it waits about thirty seconds for your next question, then ends. A session lasts at most three minutes; if an answer doesn't fit in the time left, it reads only the first part and says so, or doesn't read it, and the full answer stays in Chat. The answers are AI-generated and can be wrong. Don't put personal details in a question you type.",
|
|
1033
|
+
processing: "The answer text goes to Google to create the voice. Our server sends that voice through LiveKit to Protoface, which makes the avatar video streamed through LiveKit. Your browser connects to LiveKit, which sees your IP address.",
|
|
1034
|
+
storage: "Your microphone and camera are not used. We do not record the live session or store the generated voice. We keep a record of this consent. The privacy notice explains our safeguards, storage periods and the providers' processing. For people aged eighteen and over.",
|
|
1035
|
+
privacyLink: "How we handle this",
|
|
1036
|
+
botCheck: "off",
|
|
1037
|
+
questions: "any",
|
|
1038
|
+
followUps: true
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
],
|
|
1042
|
+
[
|
|
1043
|
+
// Stage C (BOFF-9797, the operator's decision of 2026-11-05): the mic through the browser's own speech recognition.
|
|
1044
|
+
// A DRAFT for the operator's privacy review that no environment uses yet (global-speech-svc reserves the same two
|
|
1045
|
+
// versions, and pairs them with its AVATAR_WEB_MIC=browser): web-consent-2026-11-19-d with three changes only. The
|
|
1046
|
+
// intro warns against personal details said into the microphone and says a tap on it stops the answer being read;
|
|
1047
|
+
// the processing says which speech service turns the voice into text (the browser's own, under its own terms, with
|
|
1048
|
+
// the vendors as examples) and that a live session is told of a tap and of a recording, never of the words; the
|
|
1049
|
+
// storage says the camera is not used and that the voice never reaches us, nor the text until Send, in place of
|
|
1050
|
+
// "Your microphone and camera are not used.", which is true only with no mic. The notice files of record and their
|
|
1051
|
+
// digests are a gitops change of their own, made with the review.
|
|
1052
|
+
"web-consent-2026-11-21-c",
|
|
1053
|
+
{
|
|
1054
|
+
en: {
|
|
1055
|
+
title: "Live AI avatar (beta)",
|
|
1056
|
+
introProduct: null,
|
|
1057
|
+
intro: "This is an AI avatar, not a real person. It reads aloud the answers to the questions you ask in the Avatar tab, provided each passes the speech checks. After an answer, it waits about thirty seconds for your next question, then ends. A session lasts at most three minutes; if an answer doesn't fit in the time left, it reads only the first part and says so, or doesn't read it, and the full answer stays in Chat. The answers are AI-generated and can be wrong. Don't put personal details in a question you type. Don't say them into the microphone either. Tapping the microphone while the avatar speaks stops that answer.",
|
|
1058
|
+
processing: "When you start, Cloudflare checks for bots. The answer text goes to Google to create the voice. Our server sends that voice through LiveKit to Protoface, which makes the avatar video streamed through LiveKit. Your browser connects to LiveKit, which sees your IP address. If you use the microphone, the speech service your browser uses turns your voice into text and may receive the audio: for example Google for Chrome on a computer or Android phone, Apple for Safari and on iPhone and iPad, and Microsoft for Edge on a computer, each under its own privacy terms. During a live session, our server is told when you tap the microphone and while you record (never what you say), so the avatar stops and waits.",
|
|
1059
|
+
storage: "Your camera is not used. We never receive your voice: the text appears in the question box for you to check, and none of what you said reaches us until you press Send. We do not record the live session or store the generated voice. We keep a record of this consent. The privacy notice explains our safeguards, storage periods and the providers' processing. For people aged eighteen and over.",
|
|
1060
|
+
privacyLink: "How we handle this",
|
|
1061
|
+
botCheck: "turnstile",
|
|
1062
|
+
questions: "any",
|
|
1063
|
+
followUps: true,
|
|
1064
|
+
micNotice: BROWSER_MIC_NOTICE_VERSION
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
],
|
|
1068
|
+
[
|
|
1069
|
+
// The words of web-consent-2026-11-21-c without "When you start, Cloudflare checks for bots.", for a server that runs
|
|
1070
|
+
// no bot check. A DRAFT, like it.
|
|
1071
|
+
"web-consent-2026-11-22-c",
|
|
1072
|
+
{
|
|
1073
|
+
en: {
|
|
1074
|
+
title: "Live AI avatar (beta)",
|
|
1075
|
+
introProduct: null,
|
|
1076
|
+
intro: "This is an AI avatar, not a real person. It reads aloud the answers to the questions you ask in the Avatar tab, provided each passes the speech checks. After an answer, it waits about thirty seconds for your next question, then ends. A session lasts at most three minutes; if an answer doesn't fit in the time left, it reads only the first part and says so, or doesn't read it, and the full answer stays in Chat. The answers are AI-generated and can be wrong. Don't put personal details in a question you type. Don't say them into the microphone either. Tapping the microphone while the avatar speaks stops that answer.",
|
|
1077
|
+
processing: "The answer text goes to Google to create the voice. Our server sends that voice through LiveKit to Protoface, which makes the avatar video streamed through LiveKit. Your browser connects to LiveKit, which sees your IP address. If you use the microphone, the speech service your browser uses turns your voice into text and may receive the audio: for example Google for Chrome on a computer or Android phone, Apple for Safari and on iPhone and iPad, and Microsoft for Edge on a computer, each under its own privacy terms. During a live session, our server is told when you tap the microphone and while you record (never what you say), so the avatar stops and waits.",
|
|
1078
|
+
storage: "Your camera is not used. We never receive your voice: the text appears in the question box for you to check, and none of what you said reaches us until you press Send. We do not record the live session or store the generated voice. We keep a record of this consent. The privacy notice explains our safeguards, storage periods and the providers' processing. For people aged eighteen and over.",
|
|
1079
|
+
privacyLink: "How we handle this",
|
|
1080
|
+
botCheck: "off",
|
|
1081
|
+
questions: "any",
|
|
1082
|
+
followUps: true,
|
|
1083
|
+
micNotice: BROWSER_MIC_NOTICE_VERSION
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
826
1086
|
]
|
|
827
1087
|
]);
|
|
828
1088
|
function avatarConsentCopy(version, locale) {
|
|
829
|
-
if (!
|
|
1089
|
+
if (!chunk3AIAJ2L4_js.isAvatarLiveLocaleReviewed(locale)) return null;
|
|
830
1090
|
return CONSENT.get(version)?.[locale] ?? null;
|
|
831
1091
|
}
|
|
832
1092
|
var MIC_EN = {
|
|
@@ -850,7 +1110,7 @@ var MIC_EN = {
|
|
|
850
1110
|
};
|
|
851
1111
|
var MIC_STRINGS = { en: MIC_EN };
|
|
852
1112
|
function avatarMicStrings(locale) {
|
|
853
|
-
if (!
|
|
1113
|
+
if (!chunk3AIAJ2L4_js.isAvatarLiveLocaleReviewed(locale)) return null;
|
|
854
1114
|
return MIC_STRINGS[locale] ?? null;
|
|
855
1115
|
}
|
|
856
1116
|
var AVATAR_MIC_NOTICE_VERSION = "web-mic-2026-11-05";
|
|
@@ -867,20 +1127,38 @@ var MIC_NOTICE = /* @__PURE__ */ new Map([
|
|
|
867
1127
|
privacyLink: "How we handle this"
|
|
868
1128
|
}
|
|
869
1129
|
}
|
|
1130
|
+
],
|
|
1131
|
+
[
|
|
1132
|
+
// The browser's own speech recognition (Stage C): the browser's speech service, named by example where each is
|
|
1133
|
+
// true, under its own terms; nothing of the voice reaches us, nor the text before Send; a live session told only of
|
|
1134
|
+
// a tap and of a recording. No daily limits: nothing is counted on the server.
|
|
1135
|
+
BROWSER_MIC_NOTICE_VERSION,
|
|
1136
|
+
{
|
|
1137
|
+
en: {
|
|
1138
|
+
title: "Ask with your voice",
|
|
1139
|
+
processing: "The speech service your browser uses turns your voice into text and may receive the audio: for example Google for Chrome on a computer or Android phone, Apple for Safari and on iPhone and iPad, and Microsoft for Edge on a computer. Its own privacy terms apply.",
|
|
1140
|
+
storage: "We never receive your voice, and your camera is not used.",
|
|
1141
|
+
text: "The text appears in the box for you to check. None of what you said reaches us until you press Send; once you send it, it is a chat message, kept like any other. During a live session, we are told when you tap the microphone and while you record (never what you say), so the avatar stops and waits.",
|
|
1142
|
+
limits: "Each recording stops after thirty seconds at the latest.",
|
|
1143
|
+
privacyLink: "How we handle this"
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
870
1146
|
]
|
|
871
1147
|
]);
|
|
872
1148
|
function avatarMicNotice(version, locale) {
|
|
873
|
-
if (!
|
|
1149
|
+
if (!chunk3AIAJ2L4_js.isAvatarLiveLocaleReviewed(locale)) return null;
|
|
874
1150
|
return MIC_NOTICE.get(version)?.[locale] ?? null;
|
|
875
1151
|
}
|
|
876
1152
|
var PRIVACY_HREF = "/privacy#live-avatar";
|
|
877
1153
|
var LIMIT_SECONDS = 30;
|
|
878
1154
|
var TICK_MS = 250;
|
|
879
1155
|
var IDLE = { name: "idle" };
|
|
880
|
-
var
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
])
|
|
1156
|
+
var EN_IN = { lang: "en-IN", fallbackLang: "en-US" };
|
|
1157
|
+
var BROWSER_MIC_LANGUAGES = { en: EN_IN };
|
|
1158
|
+
var FOR_GOOD = {
|
|
1159
|
+
stream: /* @__PURE__ */ new Set(["unavailable", "limit"]),
|
|
1160
|
+
browser: /* @__PURE__ */ new Set(["unsupported"])
|
|
1161
|
+
};
|
|
884
1162
|
var FAILURE_TEXT = {
|
|
885
1163
|
denied: "micDenied",
|
|
886
1164
|
unsupported: "micUnsupported",
|
|
@@ -889,14 +1167,32 @@ var FAILURE_TEXT = {
|
|
|
889
1167
|
busy: "micBusy",
|
|
890
1168
|
failed: "micFailed"
|
|
891
1169
|
};
|
|
1170
|
+
var loadedMic = null;
|
|
892
1171
|
function loadAvatarMic() {
|
|
893
|
-
return import('../avatar-mic/index.js').then((loaded) =>
|
|
894
|
-
|
|
895
|
-
|
|
1172
|
+
return import('../avatar-mic/index.js').then((loaded) => {
|
|
1173
|
+
const module = {
|
|
1174
|
+
createAvatarMic: loaded.createAvatarMic,
|
|
1175
|
+
createBrowserAvatarMic: loaded.createBrowserAvatarMic
|
|
1176
|
+
};
|
|
1177
|
+
loadedMic = module;
|
|
1178
|
+
return module;
|
|
1179
|
+
});
|
|
896
1180
|
}
|
|
897
1181
|
function canUseAvatarMic() {
|
|
898
1182
|
return typeof navigator !== "undefined" && typeof navigator.mediaDevices?.getUserMedia === "function" && typeof AudioContext !== "undefined" && typeof AudioWorkletNode !== "undefined" && typeof WebSocket !== "undefined";
|
|
899
1183
|
}
|
|
1184
|
+
function canUseBrowserMic() {
|
|
1185
|
+
if (typeof window === "undefined" || typeof navigator === "undefined") {
|
|
1186
|
+
return false;
|
|
1187
|
+
}
|
|
1188
|
+
if (window.isSecureContext !== true) return false;
|
|
1189
|
+
const recognizer = Reflect.get(window, "SpeechRecognition") ?? Reflect.get(window, "webkitSpeechRecognition");
|
|
1190
|
+
if (typeof recognizer !== "function") return false;
|
|
1191
|
+
const agent = navigator.userAgent ?? "";
|
|
1192
|
+
if (/\bFirefox\//.test(agent) || /\bFxiOS\//.test(agent)) return false;
|
|
1193
|
+
return !("brave" in navigator);
|
|
1194
|
+
}
|
|
1195
|
+
var REPLAY_PRIVATE = "rr-block rr-mask";
|
|
900
1196
|
function clock(seconds) {
|
|
901
1197
|
const whole = Math.max(0, Math.floor(seconds));
|
|
902
1198
|
return `${Math.floor(whole / 60)}:${String(whole % 60).padStart(2, "0")}`;
|
|
@@ -907,36 +1203,51 @@ function withHeardText(current, heard, maxLength) {
|
|
|
907
1203
|
const joined = current.trim() === "" ? text : `${current.replace(/\s+$/, "")} ${text}`;
|
|
908
1204
|
return joined.slice(0, Math.max(0, maxLength));
|
|
909
1205
|
}
|
|
1206
|
+
function noticeOf(source) {
|
|
1207
|
+
return source.kind === "stream" ? AVATAR_MIC_NOTICE_VERSION : source.notice;
|
|
1208
|
+
}
|
|
1209
|
+
function micKeyOf(source, locale) {
|
|
1210
|
+
return source.kind === "stream" ? `stream ${source.base}` : `browser ${BROWSER_MIC_LANGUAGES[locale]?.lang ?? ""}`;
|
|
1211
|
+
}
|
|
910
1212
|
function useAvatarMic({
|
|
911
1213
|
enabled,
|
|
912
1214
|
locale,
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
onText
|
|
1215
|
+
source,
|
|
1216
|
+
onText,
|
|
1217
|
+
onStart
|
|
917
1218
|
}) {
|
|
918
1219
|
const S = avatarMicStrings(locale);
|
|
919
|
-
const
|
|
1220
|
+
const noticeVersion = source === null ? null : noticeOf(source);
|
|
1221
|
+
const copy = noticeVersion === null ? null : avatarMicNotice(noticeVersion, locale);
|
|
1222
|
+
const kind = source?.kind ?? null;
|
|
1223
|
+
const micKey = source === null ? null : micKeyOf(source, locale);
|
|
920
1224
|
const uid = react.useId();
|
|
921
1225
|
const noticeTitleId = `${uid}-mic-notice`;
|
|
922
|
-
const [withdrawn, setWithdrawn] = react.useState(
|
|
923
|
-
const [supported, setSupported] = react.useState(false);
|
|
924
|
-
react.useEffect(
|
|
925
|
-
|
|
926
|
-
|
|
1226
|
+
const [withdrawn, setWithdrawn] = react.useState({});
|
|
1227
|
+
const [supported, setSupported] = react.useState({ stream: false, browser: false });
|
|
1228
|
+
react.useEffect(
|
|
1229
|
+
() => setSupported({ stream: canUseAvatarMic(), browser: canUseBrowserMic() }),
|
|
1230
|
+
[]
|
|
1231
|
+
);
|
|
1232
|
+
const usable = source === null ? false : source.kind === "stream" ? supported.stream : supported.browser && BROWSER_MIC_LANGUAGES[locale] !== void 0;
|
|
1233
|
+
const possible = enabled && kind !== null && usable && S !== null && copy !== null;
|
|
1234
|
+
const withdrawnReason = kind === null ? null : withdrawn[kind] ?? null;
|
|
1235
|
+
const offered = possible && withdrawnReason === null;
|
|
927
1236
|
const [state, setState] = react.useState(IDLE);
|
|
928
1237
|
const [noticeOpen, setNoticeOpen] = react.useState(false);
|
|
929
|
-
const agreedRef = react.useRef(
|
|
1238
|
+
const agreedRef = react.useRef(null);
|
|
930
1239
|
const [loading2, setLoading] = react.useState(false);
|
|
931
1240
|
const [loadFailed, setLoadFailed] = react.useState(false);
|
|
932
1241
|
const [now, setNow] = react.useState(() => Date.now());
|
|
933
|
-
const micRef = react.useRef(
|
|
1242
|
+
const micRef = react.useRef(
|
|
1243
|
+
null
|
|
1244
|
+
);
|
|
934
1245
|
const seqRef = react.useRef(0);
|
|
935
1246
|
const buttonRef = react.useRef(null);
|
|
936
1247
|
const allowRef = react.useRef(null);
|
|
937
|
-
const
|
|
938
|
-
const latest = react.useRef({
|
|
939
|
-
latest.current = {
|
|
1248
|
+
const usedRef = react.useRef(null);
|
|
1249
|
+
const latest = react.useRef({ source, locale, noticeVersion, onText, onStart });
|
|
1250
|
+
latest.current = { source, locale, noticeVersion, onText, onStart };
|
|
940
1251
|
const cancel = react.useCallback(() => {
|
|
941
1252
|
seqRef.current += 1;
|
|
942
1253
|
setLoading(false);
|
|
@@ -947,6 +1258,12 @@ function useAvatarMic({
|
|
|
947
1258
|
react.useEffect(() => {
|
|
948
1259
|
if (!offered) cancel();
|
|
949
1260
|
}, [offered, cancel]);
|
|
1261
|
+
const keyRef = react.useRef(micKey);
|
|
1262
|
+
react.useEffect(() => {
|
|
1263
|
+
if (keyRef.current === micKey) return;
|
|
1264
|
+
keyRef.current = micKey;
|
|
1265
|
+
cancel();
|
|
1266
|
+
}, [micKey, cancel]);
|
|
950
1267
|
react.useEffect(
|
|
951
1268
|
() => () => {
|
|
952
1269
|
seqRef.current += 1;
|
|
@@ -954,29 +1271,55 @@ function useAvatarMic({
|
|
|
954
1271
|
},
|
|
955
1272
|
[]
|
|
956
1273
|
);
|
|
957
|
-
const
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
1274
|
+
const micOf = react.useCallback(
|
|
1275
|
+
(loaded, wanted) => {
|
|
1276
|
+
const key = micKeyOf(wanted, latest.current.locale);
|
|
1277
|
+
const held = micRef.current;
|
|
1278
|
+
if (held !== null && held.key === key) return held.mic;
|
|
1279
|
+
held?.mic.cancel();
|
|
1280
|
+
let mic;
|
|
1281
|
+
if (wanted.kind === "stream") {
|
|
1282
|
+
const stream = () => {
|
|
1283
|
+
const current = latest.current.source;
|
|
1284
|
+
return current?.kind === "stream" ? current : null;
|
|
1285
|
+
};
|
|
1286
|
+
mic = loaded.createAvatarMic({
|
|
1287
|
+
base: wanted.base,
|
|
1288
|
+
notice: AVATAR_MIC_NOTICE_VERSION,
|
|
1289
|
+
offer: () => stream()?.offer ?? null,
|
|
1290
|
+
refreshOffer: () => stream()?.refreshOffer() ?? Promise.resolve(null),
|
|
1291
|
+
onState: setState
|
|
1292
|
+
});
|
|
1293
|
+
} else {
|
|
1294
|
+
const language = BROWSER_MIC_LANGUAGES[latest.current.locale] ?? EN_IN;
|
|
1295
|
+
mic = loaded.createBrowserAvatarMic({
|
|
1296
|
+
lang: language.lang,
|
|
1297
|
+
fallbackLang: language.fallbackLang,
|
|
1298
|
+
maxPressMs: LIMIT_SECONDS * 1e3,
|
|
1299
|
+
onState: setState
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
micRef.current = { key, kind: wanted.kind, mic };
|
|
1303
|
+
return mic;
|
|
1304
|
+
},
|
|
1305
|
+
[]
|
|
1306
|
+
);
|
|
972
1307
|
const press = react.useCallback(() => {
|
|
973
|
-
const
|
|
974
|
-
if (
|
|
1308
|
+
const wanted = latest.current.source;
|
|
1309
|
+
if (wanted === null) return;
|
|
975
1310
|
seqRef.current += 1;
|
|
976
1311
|
const seq = seqRef.current;
|
|
977
1312
|
setLoadFailed(false);
|
|
1313
|
+
const held = micRef.current;
|
|
1314
|
+
const ready = wanted.kind !== "browser" ? null : held !== null && held.key === micKeyOf(wanted, latest.current.locale) ? held.mic : loadedMic !== null ? micOf(loadedMic, wanted) : null;
|
|
1315
|
+
if (ready !== null) {
|
|
1316
|
+
setLoading(false);
|
|
1317
|
+
ready.start();
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
978
1320
|
setLoading(true);
|
|
979
|
-
|
|
1321
|
+
const arriving = held !== null && held.key === micKeyOf(wanted, latest.current.locale) ? Promise.resolve(held.mic) : loadAvatarMic().then((loaded) => micOf(loaded, wanted));
|
|
1322
|
+
void arriving.then(
|
|
980
1323
|
(mic) => {
|
|
981
1324
|
if (seqRef.current !== seq) return;
|
|
982
1325
|
setLoading(false);
|
|
@@ -988,8 +1331,9 @@ function useAvatarMic({
|
|
|
988
1331
|
setLoadFailed(true);
|
|
989
1332
|
}
|
|
990
1333
|
);
|
|
991
|
-
}, [
|
|
1334
|
+
}, [micOf]);
|
|
992
1335
|
const recording = loading2 || state.name === "starting" || state.name === "listening";
|
|
1336
|
+
const finishing = kind === "browser" && state.name === "finishing";
|
|
993
1337
|
const tap = react.useCallback(() => {
|
|
994
1338
|
if (state.name === "listening" || state.name === "starting") {
|
|
995
1339
|
micRef.current?.mic.stop();
|
|
@@ -999,15 +1343,26 @@ function useAvatarMic({
|
|
|
999
1343
|
cancel();
|
|
1000
1344
|
return;
|
|
1001
1345
|
}
|
|
1002
|
-
if (
|
|
1346
|
+
if (finishing) return;
|
|
1347
|
+
latest.current.onStart?.();
|
|
1348
|
+
if (agreedRef.current !== latest.current.noticeVersion) {
|
|
1003
1349
|
setNoticeOpen(true);
|
|
1004
|
-
|
|
1350
|
+
const seq = seqRef.current;
|
|
1351
|
+
void loadAvatarMic().then(
|
|
1352
|
+
(loaded) => {
|
|
1353
|
+
const wanted = latest.current.source;
|
|
1354
|
+
if (seqRef.current === seq && wanted?.kind === "browser") {
|
|
1355
|
+
micOf(loaded, wanted);
|
|
1356
|
+
}
|
|
1357
|
+
},
|
|
1358
|
+
() => void 0
|
|
1359
|
+
);
|
|
1005
1360
|
return;
|
|
1006
1361
|
}
|
|
1007
1362
|
press();
|
|
1008
|
-
}, [cancel, loading2, press, state.name]);
|
|
1363
|
+
}, [cancel, finishing, loading2, micOf, press, state.name]);
|
|
1009
1364
|
const allow = react.useCallback(() => {
|
|
1010
|
-
agreedRef.current =
|
|
1365
|
+
agreedRef.current = latest.current.noticeVersion;
|
|
1011
1366
|
setNoticeOpen(false);
|
|
1012
1367
|
buttonRef.current?.focus();
|
|
1013
1368
|
press();
|
|
@@ -1027,17 +1382,27 @@ function useAvatarMic({
|
|
|
1027
1382
|
return () => clearInterval(timer);
|
|
1028
1383
|
}, [listeningSince]);
|
|
1029
1384
|
react.useEffect(() => {
|
|
1030
|
-
if (state.name === "done" && state
|
|
1031
|
-
|
|
1385
|
+
if (state.name === "done" && state !== usedRef.current) {
|
|
1386
|
+
usedRef.current = state;
|
|
1032
1387
|
if (state.text !== "") latest.current.onText(state.text);
|
|
1033
|
-
|
|
1034
|
-
|
|
1388
|
+
return;
|
|
1389
|
+
}
|
|
1390
|
+
const pressed = micRef.current?.kind;
|
|
1391
|
+
if (state.name === "failed" && pressed !== void 0 && FOR_GOOD[pressed].has(state.reason)) {
|
|
1392
|
+
const reason = state.reason;
|
|
1393
|
+
setWithdrawn((held) => ({ ...held, [pressed]: reason }));
|
|
1035
1394
|
}
|
|
1036
1395
|
}, [state]);
|
|
1037
1396
|
if (!possible || S === null || copy === null) {
|
|
1038
|
-
return {
|
|
1397
|
+
return {
|
|
1398
|
+
button: null,
|
|
1399
|
+
notice: null,
|
|
1400
|
+
lines: null,
|
|
1401
|
+
cancel,
|
|
1402
|
+
recording: false
|
|
1403
|
+
};
|
|
1039
1404
|
}
|
|
1040
|
-
const status =
|
|
1405
|
+
const status = withdrawnReason !== null ? S[FAILURE_TEXT[withdrawnReason]] : 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
1406
|
const heard = offered && (state.name === "listening" || state.name === "finishing") ? state.text : "";
|
|
1042
1407
|
const button = !offered ? null : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1043
1408
|
"button",
|
|
@@ -1046,12 +1411,15 @@ function useAvatarMic({
|
|
|
1046
1411
|
type: "button",
|
|
1047
1412
|
"aria-label": recording ? S.micStop : S.micStart,
|
|
1048
1413
|
"aria-pressed": recording,
|
|
1414
|
+
"aria-disabled": finishing ? true : void 0,
|
|
1049
1415
|
title: recording ? S.micStop : S.micStart,
|
|
1050
1416
|
"data-boff-explore": "avatar-live-mic",
|
|
1051
1417
|
"data-mic": loading2 ? "starting" : state.name,
|
|
1418
|
+
"data-mic-source": kind ?? void 0,
|
|
1052
1419
|
className: chunkC6OJIHNO_js.cn(
|
|
1053
1420
|
"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"
|
|
1421
|
+
recording ? "border-primary bg-primary text-primary-foreground" : "border-border bg-background text-foreground hover:bg-accent hover:text-accent-foreground",
|
|
1422
|
+
finishing && "cursor-wait opacity-50"
|
|
1055
1423
|
),
|
|
1056
1424
|
onClick: tap,
|
|
1057
1425
|
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" })
|
|
@@ -1134,12 +1502,22 @@ function useAvatarMic({
|
|
|
1134
1502
|
{
|
|
1135
1503
|
"data-boff-explore": "avatar-live-mic-heard",
|
|
1136
1504
|
dir: "auto",
|
|
1137
|
-
className:
|
|
1505
|
+
className: chunkC6OJIHNO_js.cn(
|
|
1506
|
+
// rrweb's default block and mask classes: a site's session replay records none of what is heard.
|
|
1507
|
+
REPLAY_PRIVATE,
|
|
1508
|
+
"line-clamp-3 break-words text-sm italic text-muted-foreground"
|
|
1509
|
+
),
|
|
1138
1510
|
children: heard
|
|
1139
1511
|
}
|
|
1140
1512
|
)
|
|
1141
1513
|
] });
|
|
1142
|
-
return {
|
|
1514
|
+
return {
|
|
1515
|
+
button,
|
|
1516
|
+
notice,
|
|
1517
|
+
lines,
|
|
1518
|
+
cancel,
|
|
1519
|
+
recording: offered && state.name === "listening"
|
|
1520
|
+
};
|
|
1143
1521
|
}
|
|
1144
1522
|
|
|
1145
1523
|
// src/components/explore-avatar-turnstile.ts
|
|
@@ -1262,6 +1640,13 @@ var END_GRACE_MS = 1e4;
|
|
|
1262
1640
|
var CLOCK_TOLERANCE_MS = 5e3;
|
|
1263
1641
|
var UNHEARD_GRACE_MS = 5e3;
|
|
1264
1642
|
var PLAYOUT_TAIL_MS = 5e3;
|
|
1643
|
+
var QUIET_PLAYED_OUT_MS = 2500;
|
|
1644
|
+
var OWED_VOICE_WAIT_MS = 15e3;
|
|
1645
|
+
var LISTENING_MARK_MS = 1e4;
|
|
1646
|
+
var LISTENING_MARK_SPACING_MS = 5e3;
|
|
1647
|
+
var BUSY_RETRY_MS = 2e3;
|
|
1648
|
+
var BUSY_RETRY_MAX_MS = 8e3;
|
|
1649
|
+
var MAX_BUSY_RETRIES = 24;
|
|
1265
1650
|
var SERVICE_MAX_AGE_MS = 15 * 6e4;
|
|
1266
1651
|
var ANSWER_OFFER_MS = SERVICE_MAX_AGE_MS - 6e4;
|
|
1267
1652
|
var MAX_TIMER_MS = 2147483647;
|
|
@@ -1276,6 +1661,12 @@ function stopAttending(handle) {
|
|
|
1276
1661
|
if (handle.tail !== null) clearTimeout(handle.tail);
|
|
1277
1662
|
handle.unheard = null;
|
|
1278
1663
|
handle.tail = null;
|
|
1664
|
+
const run = handle.followUps;
|
|
1665
|
+
if (run === null) return;
|
|
1666
|
+
if (run.quiet !== null) clearTimeout(run.quiet);
|
|
1667
|
+
if (run.retry !== null) clearTimeout(run.retry);
|
|
1668
|
+
run.quiet = null;
|
|
1669
|
+
run.retry = null;
|
|
1279
1670
|
}
|
|
1280
1671
|
var REFUSAL_TEXT = {
|
|
1281
1672
|
busy: "busy",
|
|
@@ -1286,6 +1677,18 @@ var REFUSAL_TEXT = {
|
|
|
1286
1677
|
not_eligible: "notEligible",
|
|
1287
1678
|
answer_claimed: "answerClaimed"
|
|
1288
1679
|
};
|
|
1680
|
+
var FOLLOW_UP_TEXT = {
|
|
1681
|
+
no_time: "followUpNoTime",
|
|
1682
|
+
max_answers: "followUpMaxAnswers",
|
|
1683
|
+
// The same line as a first answer the checks refuse: it stays in Chat as text.
|
|
1684
|
+
not_eligible: "notSpoken",
|
|
1685
|
+
answer_claimed: "answerClaimed",
|
|
1686
|
+
limits: "followUpLimits",
|
|
1687
|
+
paused: "followUpPaused",
|
|
1688
|
+
busy: "followUpUnread",
|
|
1689
|
+
answer_failed: "answerFailed",
|
|
1690
|
+
failed: "followUpUnread"
|
|
1691
|
+
};
|
|
1289
1692
|
var EMPTY = {
|
|
1290
1693
|
kind: "empty",
|
|
1291
1694
|
answerId: null,
|
|
@@ -1360,10 +1763,21 @@ function inFlight(conversation) {
|
|
|
1360
1763
|
return conversation?.phase === "sending" || conversation?.phase === "streaming";
|
|
1361
1764
|
}
|
|
1362
1765
|
function acceptsQuestion(slot) {
|
|
1363
|
-
|
|
1766
|
+
if (!slot.any || slot.textMode || slot.reason !== null) return false;
|
|
1767
|
+
return slot.phase === "needs-answer" ? slot.kind === "empty" : slot.phase === "live" && slot.followUp;
|
|
1364
1768
|
}
|
|
1365
|
-
function
|
|
1366
|
-
return
|
|
1769
|
+
function followUpBox(phase, run, token) {
|
|
1770
|
+
return phase.name === "live" && run !== null && phase.session.followUp !== null && !phase.session.followUp.closed && token === run.token;
|
|
1771
|
+
}
|
|
1772
|
+
var NO_MIC = { kind: "none" };
|
|
1773
|
+
function micChoiceOf(result, locale) {
|
|
1774
|
+
if (result.kind !== "available") return NO_MIC;
|
|
1775
|
+
const { offer } = result;
|
|
1776
|
+
const micNotice = avatarConsentCopy(offer.consentVersion, locale)?.micNotice;
|
|
1777
|
+
if (offer.mic !== void 0) {
|
|
1778
|
+
return offer.mic.mode === "browser" && micNotice !== void 0 ? { kind: "browser", notice: micNotice } : NO_MIC;
|
|
1779
|
+
}
|
|
1780
|
+
return offer.stt !== void 0 && micNotice === void 0 ? { kind: "stream", offer: { ...offer.stt, receivedAt: Date.now() } } : NO_MIC;
|
|
1367
1781
|
}
|
|
1368
1782
|
function utcToday() {
|
|
1369
1783
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -1513,8 +1927,9 @@ function AvatarLiveView({
|
|
|
1513
1927
|
const waitRef = react.useRef(null);
|
|
1514
1928
|
const sendingRef = react.useRef(false);
|
|
1515
1929
|
const textBoxRef = react.useRef(false);
|
|
1516
|
-
const [
|
|
1930
|
+
const [micChoice, setMicChoice] = react.useState(NO_MIC);
|
|
1517
1931
|
const micCancelRef = react.useRef(() => void 0);
|
|
1932
|
+
const sendDueRef = react.useRef(() => void 0);
|
|
1518
1933
|
const checkRef = react.useRef(null);
|
|
1519
1934
|
const askedLocale = react.useRef(locale);
|
|
1520
1935
|
const startRef = react.useRef(null);
|
|
@@ -1603,10 +2018,13 @@ function AvatarLiveView({
|
|
|
1603
2018
|
if (!handle || handle.done || latest.current.live.endUnattended !== true) {
|
|
1604
2019
|
return;
|
|
1605
2020
|
}
|
|
1606
|
-
const
|
|
1607
|
-
if (
|
|
2021
|
+
const shown2 = phaseRef.current;
|
|
2022
|
+
if (shown2.name !== "pending" && shown2.name !== "live") return;
|
|
2023
|
+
const current = shown2.name;
|
|
1608
2024
|
const away = !inSight();
|
|
1609
|
-
|
|
2025
|
+
const followUp2 = shown2.session.followUp;
|
|
2026
|
+
const asking = followUp2 !== null && followUp2.pending !== null;
|
|
2027
|
+
if (away && handle.playedOut && current === "live" && !asking) {
|
|
1610
2028
|
finish("finished", "user");
|
|
1611
2029
|
return;
|
|
1612
2030
|
}
|
|
@@ -1625,6 +2043,184 @@ function AvatarLiveView({
|
|
|
1625
2043
|
}
|
|
1626
2044
|
}, UNHEARD_GRACE_MS);
|
|
1627
2045
|
}, [finish, inSight]);
|
|
2046
|
+
const showFollowUp = react.useCallback(
|
|
2047
|
+
(handle, next, spoken) => {
|
|
2048
|
+
const current = phaseRef.current;
|
|
2049
|
+
if (sessionRef.current !== handle || handle.done || current.name !== "pending" && current.name !== "live" || current.session.followUp === null) {
|
|
2050
|
+
return;
|
|
2051
|
+
}
|
|
2052
|
+
const session = {
|
|
2053
|
+
...current.session,
|
|
2054
|
+
...spoken,
|
|
2055
|
+
followUp: { ...current.session.followUp, ...next }
|
|
2056
|
+
};
|
|
2057
|
+
go(
|
|
2058
|
+
current.name === "live" ? { name: "live", session } : { name: "pending", session }
|
|
2059
|
+
);
|
|
2060
|
+
},
|
|
2061
|
+
[go]
|
|
2062
|
+
);
|
|
2063
|
+
const tell = react.useCallback(
|
|
2064
|
+
(handle, kind2) => {
|
|
2065
|
+
if (handle.done || handle.followUps === null) return;
|
|
2066
|
+
void requestTurn({
|
|
2067
|
+
base: handle.base,
|
|
2068
|
+
headers: handle.headers,
|
|
2069
|
+
sessionId: handle.session.sessionId,
|
|
2070
|
+
secret: handle.session.endSecret,
|
|
2071
|
+
kind: kind2
|
|
2072
|
+
});
|
|
2073
|
+
},
|
|
2074
|
+
[]
|
|
2075
|
+
);
|
|
2076
|
+
const whenQuiet = react.useCallback(
|
|
2077
|
+
(handle, delay) => {
|
|
2078
|
+
const run = handle.followUps;
|
|
2079
|
+
if (run === null) return;
|
|
2080
|
+
const wait = (ms) => {
|
|
2081
|
+
if (run.quiet !== null) clearTimeout(run.quiet);
|
|
2082
|
+
run.quiet = setTimeout(() => {
|
|
2083
|
+
run.quiet = null;
|
|
2084
|
+
if (sessionRef.current !== handle || handle.done || run.voice) {
|
|
2085
|
+
return;
|
|
2086
|
+
}
|
|
2087
|
+
if (run.owed) {
|
|
2088
|
+
run.owed = false;
|
|
2089
|
+
wait(OWED_VOICE_WAIT_MS);
|
|
2090
|
+
return;
|
|
2091
|
+
}
|
|
2092
|
+
handle.playedOut = true;
|
|
2093
|
+
showFollowUp(handle, { speaking: false });
|
|
2094
|
+
attend();
|
|
2095
|
+
sendDueRef.current(handle);
|
|
2096
|
+
}, ms);
|
|
2097
|
+
};
|
|
2098
|
+
wait(delay);
|
|
2099
|
+
},
|
|
2100
|
+
[attend, showFollowUp]
|
|
2101
|
+
);
|
|
2102
|
+
const heardVoice = react.useCallback(
|
|
2103
|
+
(handle, speaking) => {
|
|
2104
|
+
const run = handle.followUps;
|
|
2105
|
+
if (run === null) return;
|
|
2106
|
+
run.voice = speaking;
|
|
2107
|
+
if (!speaking) {
|
|
2108
|
+
whenQuiet(handle, QUIET_PLAYED_OUT_MS);
|
|
2109
|
+
return;
|
|
2110
|
+
}
|
|
2111
|
+
if (run.quiet !== null) clearTimeout(run.quiet);
|
|
2112
|
+
run.quiet = null;
|
|
2113
|
+
run.heard = true;
|
|
2114
|
+
run.starts += 1;
|
|
2115
|
+
run.owed = false;
|
|
2116
|
+
handle.playedOut = false;
|
|
2117
|
+
showFollowUp(handle, { speaking: true });
|
|
2118
|
+
},
|
|
2119
|
+
[showFollowUp, whenQuiet]
|
|
2120
|
+
);
|
|
2121
|
+
const sendDue = react.useCallback(
|
|
2122
|
+
(handle) => {
|
|
2123
|
+
const run = handle.followUps;
|
|
2124
|
+
const due = run?.due ?? null;
|
|
2125
|
+
if (run === null || due === null || due.sending || run.retry !== null || handle.done || sessionRef.current !== handle || run.heard && !handle.playedOut) {
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
due.sending = true;
|
|
2129
|
+
const starts = run.starts;
|
|
2130
|
+
const read = () => requestTurn({
|
|
2131
|
+
base: handle.base,
|
|
2132
|
+
headers: handle.headers,
|
|
2133
|
+
sessionId: handle.session.sessionId,
|
|
2134
|
+
secret: handle.session.endSecret,
|
|
2135
|
+
kind: "answer",
|
|
2136
|
+
messageId: due.messageId,
|
|
2137
|
+
idempotencyKey: due.idempotencyKey
|
|
2138
|
+
});
|
|
2139
|
+
const notRead = (unread, closed) => {
|
|
2140
|
+
run.due = null;
|
|
2141
|
+
showFollowUp(handle, {
|
|
2142
|
+
pending: null,
|
|
2143
|
+
unread: due.known ?? unread,
|
|
2144
|
+
...closed ? { closed } : {}
|
|
2145
|
+
});
|
|
2146
|
+
attend();
|
|
2147
|
+
};
|
|
2148
|
+
void (async () => {
|
|
2149
|
+
let result = await read();
|
|
2150
|
+
if (result.kind === "failed" && result.lost && !handle.done) {
|
|
2151
|
+
result = await read();
|
|
2152
|
+
}
|
|
2153
|
+
due.sending = false;
|
|
2154
|
+
if (sessionRef.current !== handle || handle.done || run.due !== due) {
|
|
2155
|
+
return;
|
|
2156
|
+
}
|
|
2157
|
+
if (result.kind === "refused" && result.reason === "busy" && due.busy < MAX_BUSY_RETRIES) {
|
|
2158
|
+
due.busy += 1;
|
|
2159
|
+
if (run.heard && !handle.playedOut) return;
|
|
2160
|
+
const delay = Math.max(
|
|
2161
|
+
result.retryAfterSeconds === void 0 ? BUSY_RETRY_MS : result.retryAfterSeconds * 1e3,
|
|
2162
|
+
Math.min(BUSY_RETRY_MAX_MS, BUSY_RETRY_MS * 2 ** (due.busy - 1))
|
|
2163
|
+
);
|
|
2164
|
+
if (Date.now() + delay >= handle.endsAt) {
|
|
2165
|
+
notRead("no_time", false);
|
|
2166
|
+
return;
|
|
2167
|
+
}
|
|
2168
|
+
run.retry = setTimeout(() => {
|
|
2169
|
+
run.retry = null;
|
|
2170
|
+
sendDueRef.current(handle);
|
|
2171
|
+
}, delay);
|
|
2172
|
+
return;
|
|
2173
|
+
}
|
|
2174
|
+
if (result.kind !== "accepted") {
|
|
2175
|
+
notRead(
|
|
2176
|
+
result.kind === "refused" ? result.reason : "failed",
|
|
2177
|
+
result.kind === "gone" || result.kind === "refused" && result.reason === "max_answers"
|
|
2178
|
+
);
|
|
2179
|
+
return;
|
|
2180
|
+
}
|
|
2181
|
+
run.due = null;
|
|
2182
|
+
run.answers += 1;
|
|
2183
|
+
const heardSince = run.starts !== starts;
|
|
2184
|
+
run.owed = !heardSince;
|
|
2185
|
+
const playing = !heardSince || run.voice || run.quiet !== null;
|
|
2186
|
+
if (playing) handle.playedOut = false;
|
|
2187
|
+
showFollowUp(
|
|
2188
|
+
handle,
|
|
2189
|
+
{
|
|
2190
|
+
speaking: playing,
|
|
2191
|
+
pending: null,
|
|
2192
|
+
unread: null,
|
|
2193
|
+
closed: run.answers >= run.limits.maxAnswers
|
|
2194
|
+
},
|
|
2195
|
+
{ captions: result.captions, excerpt: result.excerpt }
|
|
2196
|
+
);
|
|
2197
|
+
if (!playing) {
|
|
2198
|
+
attend();
|
|
2199
|
+
return;
|
|
2200
|
+
}
|
|
2201
|
+
if (run.owed && run.heard && !run.voice) whenQuiet(handle, 0);
|
|
2202
|
+
})();
|
|
2203
|
+
},
|
|
2204
|
+
[attend, showFollowUp, whenQuiet]
|
|
2205
|
+
);
|
|
2206
|
+
sendDueRef.current = sendDue;
|
|
2207
|
+
const readFollowUp = react.useCallback(
|
|
2208
|
+
(handle, messageId, known) => {
|
|
2209
|
+
const run = handle.followUps;
|
|
2210
|
+
if (run === null) return;
|
|
2211
|
+
run.sent.add(messageId);
|
|
2212
|
+
const idempotencyKey = newIdempotencyKey();
|
|
2213
|
+
if (idempotencyKey === null) {
|
|
2214
|
+
showFollowUp(handle, { pending: null, unread: known ?? "failed" });
|
|
2215
|
+
attend();
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2218
|
+
run.due = { messageId, idempotencyKey, known, busy: 0, sending: false };
|
|
2219
|
+
showFollowUp(handle, { pending: "reading", unread: null });
|
|
2220
|
+
sendDue(handle);
|
|
2221
|
+
},
|
|
2222
|
+
[attend, sendDue, showFollowUp]
|
|
2223
|
+
);
|
|
1628
2224
|
const cancelStart = react.useCallback((reason) => {
|
|
1629
2225
|
const run = startRef.current;
|
|
1630
2226
|
if (!run) return;
|
|
@@ -1635,7 +2231,7 @@ function AvatarLiveView({
|
|
|
1635
2231
|
const applyAvailability = react.useCallback(
|
|
1636
2232
|
(result, locale2) => {
|
|
1637
2233
|
const { live: live2 } = latest.current;
|
|
1638
|
-
|
|
2234
|
+
setMicChoice(micChoiceOf(result, locale2));
|
|
1639
2235
|
if (result.kind === "inert") {
|
|
1640
2236
|
setMode("suggested");
|
|
1641
2237
|
go(INERT2);
|
|
@@ -1653,7 +2249,7 @@ function AvatarLiveView({
|
|
|
1653
2249
|
go(INERT2);
|
|
1654
2250
|
return;
|
|
1655
2251
|
}
|
|
1656
|
-
if (result.offer.botCheck === "turnstile" &&
|
|
2252
|
+
if (result.offer.botCheck === "turnstile" && chunk3AIAJ2L4_js.turnstileSiteKeyOf(live2) === null) {
|
|
1657
2253
|
setMode("suggested");
|
|
1658
2254
|
go(INERT2);
|
|
1659
2255
|
return;
|
|
@@ -1688,16 +2284,33 @@ function AvatarLiveView({
|
|
|
1688
2284
|
});
|
|
1689
2285
|
}, [applyAvailability, go]);
|
|
1690
2286
|
const begin = react.useCallback(
|
|
1691
|
-
(session,
|
|
2287
|
+
(session, context) => {
|
|
2288
|
+
const { offer } = context;
|
|
1692
2289
|
const received = Date.now();
|
|
1693
2290
|
const longest = offer.maxSessionSeconds * 1e3;
|
|
1694
2291
|
const ahead = session.endsAt - received;
|
|
1695
2292
|
const endsAt = ahead > 0 && ahead <= longest + CLOCK_TOLERANCE_MS ? session.endsAt : received + longest;
|
|
2293
|
+
const followUps = session.followUps !== void 0 && offer.copy.followUps === true && offer.questions === "any" ? {
|
|
2294
|
+
limits: session.followUps,
|
|
2295
|
+
token: context.conversationToken,
|
|
2296
|
+
answers: 1,
|
|
2297
|
+
voice: false,
|
|
2298
|
+
heard: false,
|
|
2299
|
+
starts: 0,
|
|
2300
|
+
owed: true,
|
|
2301
|
+
quiet: null,
|
|
2302
|
+
asked: null,
|
|
2303
|
+
due: null,
|
|
2304
|
+
retry: null,
|
|
2305
|
+
sent: /* @__PURE__ */ new Set([context.messageId]),
|
|
2306
|
+
seq: 0,
|
|
2307
|
+
listenedAt: null
|
|
2308
|
+
} : null;
|
|
1696
2309
|
sessionRef.current = {
|
|
1697
2310
|
session,
|
|
1698
|
-
base:
|
|
1699
|
-
headers:
|
|
1700
|
-
module,
|
|
2311
|
+
base: context.base,
|
|
2312
|
+
headers: context.headers,
|
|
2313
|
+
module: context.module,
|
|
1701
2314
|
room: null,
|
|
1702
2315
|
joined: null,
|
|
1703
2316
|
endsAt,
|
|
@@ -1707,6 +2320,7 @@ function AvatarLiveView({
|
|
|
1707
2320
|
playedOut: false,
|
|
1708
2321
|
unheard: null,
|
|
1709
2322
|
tail: null,
|
|
2323
|
+
followUps,
|
|
1710
2324
|
done: false
|
|
1711
2325
|
};
|
|
1712
2326
|
setBlocked(false);
|
|
@@ -1716,7 +2330,13 @@ function AvatarLiveView({
|
|
|
1716
2330
|
session: {
|
|
1717
2331
|
endsAt,
|
|
1718
2332
|
captions: session.captions,
|
|
1719
|
-
excerpt: session.excerpt
|
|
2333
|
+
excerpt: session.excerpt,
|
|
2334
|
+
followUp: followUps === null ? null : {
|
|
2335
|
+
speaking: true,
|
|
2336
|
+
pending: null,
|
|
2337
|
+
unread: null,
|
|
2338
|
+
closed: followUps.answers >= followUps.limits.maxAnswers
|
|
2339
|
+
}
|
|
1720
2340
|
}
|
|
1721
2341
|
});
|
|
1722
2342
|
},
|
|
@@ -1748,7 +2368,7 @@ function AvatarLiveView({
|
|
|
1748
2368
|
}
|
|
1749
2369
|
switch (result.kind) {
|
|
1750
2370
|
case "created":
|
|
1751
|
-
begin(result.session, context
|
|
2371
|
+
begin(result.session, context);
|
|
1752
2372
|
return;
|
|
1753
2373
|
case "unusable":
|
|
1754
2374
|
sendEnd({
|
|
@@ -1790,7 +2410,7 @@ function AvatarLiveView({
|
|
|
1790
2410
|
return;
|
|
1791
2411
|
}
|
|
1792
2412
|
const refusedFrom = exchangeAtStart.offerUntil - ANSWER_OFFER_MS + SERVICE_MAX_AGE_MS;
|
|
1793
|
-
const siteKey = offer.botCheck === "turnstile" ?
|
|
2413
|
+
const siteKey = offer.botCheck === "turnstile" ? chunk3AIAJ2L4_js.turnstileSiteKeyOf(live2) : null;
|
|
1794
2414
|
if (offer.botCheck === "turnstile" && (siteKey === null || !canBindBotCheck())) {
|
|
1795
2415
|
go(INERT2);
|
|
1796
2416
|
return;
|
|
@@ -1846,7 +2466,14 @@ function AvatarLiveView({
|
|
|
1846
2466
|
return;
|
|
1847
2467
|
}
|
|
1848
2468
|
if (turnstile !== null) go({ name: "creating" });
|
|
1849
|
-
const context = {
|
|
2469
|
+
const context = {
|
|
2470
|
+
module: loaded,
|
|
2471
|
+
offer,
|
|
2472
|
+
messageId,
|
|
2473
|
+
conversationToken,
|
|
2474
|
+
base: base2,
|
|
2475
|
+
headers: headers2
|
|
2476
|
+
};
|
|
1850
2477
|
const result = await requestCreate({
|
|
1851
2478
|
base: base2,
|
|
1852
2479
|
headers: headers2,
|
|
@@ -2151,6 +2778,9 @@ function AvatarLiveView({
|
|
|
2151
2778
|
attend();
|
|
2152
2779
|
}, PLAYOUT_TAIL_MS);
|
|
2153
2780
|
return;
|
|
2781
|
+
case "speaking":
|
|
2782
|
+
heardVoice(handle, event.speaking);
|
|
2783
|
+
return;
|
|
2154
2784
|
case "speaker-left":
|
|
2155
2785
|
finish(
|
|
2156
2786
|
wasLive ? "finished" : "failed",
|
|
@@ -2163,7 +2793,7 @@ function AvatarLiveView({
|
|
|
2163
2793
|
return;
|
|
2164
2794
|
}
|
|
2165
2795
|
},
|
|
2166
|
-
[attend, finish, goLive, inSight]
|
|
2796
|
+
[attend, finish, goLive, heardVoice, inSight]
|
|
2167
2797
|
);
|
|
2168
2798
|
react.useEffect(() => {
|
|
2169
2799
|
if (phase.name !== "pending") return;
|
|
@@ -2299,20 +2929,28 @@ function AvatarLiveView({
|
|
|
2299
2929
|
const textMode = any && (usedToday && (phase.name === "needs-answer" || phase.name === "consent" || phase.name === "declined" || phase.name === "ended") || phase.name === "refused" && phase.reason === "limits");
|
|
2300
2930
|
textBoxRef.current = textMode;
|
|
2301
2931
|
const askState = !any ? void 0 : textMode ? "text" : phase.name === "needs-answer" ? kind === "empty" ? "ready" : kind : phase.name;
|
|
2302
|
-
const
|
|
2932
|
+
const followUp = phase.name === "live" ? phase.session.followUp : null;
|
|
2933
|
+
const followUpAsks = followUpBox(
|
|
2934
|
+
phase,
|
|
2935
|
+
sessionRef.current?.followUps ?? null,
|
|
2936
|
+
token
|
|
2937
|
+
);
|
|
2938
|
+
const askReason = inFlight(conversation) || followUp !== null && followUp.pending !== null ? S.askBusy : conversation?.canSend ? null : S.askUnavailable;
|
|
2303
2939
|
const refreshMicOffer = react.useCallback(async () => {
|
|
2304
2940
|
const { live: live2, locale: locale2, base: base2, headers: headers2 } = latest.current;
|
|
2305
2941
|
if (base2 === null) return null;
|
|
2306
|
-
const next =
|
|
2942
|
+
const next = micChoiceOf(
|
|
2307
2943
|
await requestAvailabilityAnswer({
|
|
2308
2944
|
base: base2,
|
|
2309
2945
|
headers: headers2,
|
|
2310
2946
|
site: live2.site,
|
|
2311
2947
|
locale: locale2
|
|
2312
|
-
})
|
|
2948
|
+
}),
|
|
2949
|
+
locale2
|
|
2313
2950
|
);
|
|
2314
|
-
if (next !==
|
|
2315
|
-
|
|
2951
|
+
if (next.kind !== "stream") return null;
|
|
2952
|
+
setMicChoice(next);
|
|
2953
|
+
return next.offer;
|
|
2316
2954
|
}, []);
|
|
2317
2955
|
const onMicText = react.useCallback(
|
|
2318
2956
|
(text) => {
|
|
@@ -2324,21 +2962,66 @@ function AvatarLiveView({
|
|
|
2324
2962
|
},
|
|
2325
2963
|
[askMaxLength, touchFirst]
|
|
2326
2964
|
);
|
|
2965
|
+
const onMicStart = react.useCallback(() => {
|
|
2966
|
+
const handle = sessionRef.current;
|
|
2967
|
+
const current = phaseRef.current;
|
|
2968
|
+
if (handle === null || handle.followUps === null || handle.done || current.name !== "live" || current.session.followUp?.speaking !== true) {
|
|
2969
|
+
return;
|
|
2970
|
+
}
|
|
2971
|
+
const run = handle.followUps;
|
|
2972
|
+
tell(handle, "interrupt");
|
|
2973
|
+
if (run.quiet !== null) clearTimeout(run.quiet);
|
|
2974
|
+
run.quiet = null;
|
|
2975
|
+
run.owed = false;
|
|
2976
|
+
handle.playedOut = true;
|
|
2977
|
+
showFollowUp(handle, { speaking: false });
|
|
2978
|
+
}, [showFollowUp, tell]);
|
|
2979
|
+
const micSource = micChoice.kind === "browser" ? { kind: "browser", notice: micChoice.notice } : micChoice.kind === "stream" && base !== null ? {
|
|
2980
|
+
kind: "stream",
|
|
2981
|
+
base,
|
|
2982
|
+
offer: micChoice.offer,
|
|
2983
|
+
refreshOffer: refreshMicOffer
|
|
2984
|
+
} : null;
|
|
2327
2985
|
const mic = useAvatarMic({
|
|
2328
|
-
enabled: live.mic === true &&
|
|
2986
|
+
enabled: live.mic === true && micSource !== null && !off && acceptsQuestion({
|
|
2329
2987
|
any,
|
|
2330
2988
|
phase: phase.name,
|
|
2331
2989
|
kind,
|
|
2332
2990
|
textMode,
|
|
2333
|
-
reason: askReason
|
|
2991
|
+
reason: askReason,
|
|
2992
|
+
followUp: followUpAsks
|
|
2334
2993
|
}),
|
|
2335
2994
|
locale,
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
onText: onMicText
|
|
2995
|
+
source: micSource,
|
|
2996
|
+
onText: onMicText,
|
|
2997
|
+
onStart: onMicStart
|
|
2340
2998
|
});
|
|
2341
2999
|
micCancelRef.current = mic.cancel;
|
|
3000
|
+
const micRecording = mic.recording;
|
|
3001
|
+
react.useEffect(() => {
|
|
3002
|
+
const handle = sessionRef.current;
|
|
3003
|
+
if (!micRecording || handle === null || handle.followUps === null || handle.done) {
|
|
3004
|
+
return;
|
|
3005
|
+
}
|
|
3006
|
+
const run = handle.followUps;
|
|
3007
|
+
const every = Math.min(
|
|
3008
|
+
LISTENING_MARK_MS,
|
|
3009
|
+
Math.max(1e3, run.limits.idleSeconds * 1e3 / 3)
|
|
3010
|
+
);
|
|
3011
|
+
const spacing = Math.min(LISTENING_MARK_SPACING_MS, every / 2);
|
|
3012
|
+
const mark = () => {
|
|
3013
|
+
const at = Date.now();
|
|
3014
|
+
if (run.listenedAt !== null && at - run.listenedAt < spacing) return;
|
|
3015
|
+
run.listenedAt = at;
|
|
3016
|
+
tell(handle, "listening");
|
|
3017
|
+
};
|
|
3018
|
+
mark();
|
|
3019
|
+
const timer = setInterval(mark, every);
|
|
3020
|
+
return () => {
|
|
3021
|
+
clearInterval(timer);
|
|
3022
|
+
if (sessionRef.current === handle) mark();
|
|
3023
|
+
};
|
|
3024
|
+
}, [micRecording, tell]);
|
|
2342
3025
|
const notNow = react.useCallback(() => {
|
|
2343
3026
|
const current = phaseRef.current;
|
|
2344
3027
|
if ((current.name === "consent" || current.name === "needs-answer") && current.offer.questions === "any") {
|
|
@@ -2347,6 +3030,43 @@ function AvatarLiveView({
|
|
|
2347
3030
|
}
|
|
2348
3031
|
onContinueInChat();
|
|
2349
3032
|
}, [go, onContinueInChat]);
|
|
3033
|
+
const askFollowUp = react.useCallback(
|
|
3034
|
+
(handle, conversation2, question) => {
|
|
3035
|
+
const run = handle.followUps;
|
|
3036
|
+
const current = phaseRef.current;
|
|
3037
|
+
if (run === null || handle.done || current.name !== "live" || current.session.followUp?.pending !== null || !followUpBox(current, run, conversation2.getConversationToken())) {
|
|
3038
|
+
return;
|
|
3039
|
+
}
|
|
3040
|
+
sendingRef.current = true;
|
|
3041
|
+
run.seq += 1;
|
|
3042
|
+
const seq = run.seq;
|
|
3043
|
+
setDraft("");
|
|
3044
|
+
tell(handle, "asking");
|
|
3045
|
+
showFollowUp(handle, { pending: "asking", unread: null });
|
|
3046
|
+
void conversation2.send(question, [], { surface: "AVATAR" }).then((added) => {
|
|
3047
|
+
if (added === null) {
|
|
3048
|
+
setDraft((current2) => current2 === "" ? question : current2);
|
|
3049
|
+
}
|
|
3050
|
+
if (sessionRef.current !== handle || handle.done || run.seq !== seq) {
|
|
3051
|
+
return;
|
|
3052
|
+
}
|
|
3053
|
+
if (added === null || added.conversationToken !== run.token) {
|
|
3054
|
+
showFollowUp(handle, { pending: null });
|
|
3055
|
+
attend();
|
|
3056
|
+
return;
|
|
3057
|
+
}
|
|
3058
|
+
run.asked = {
|
|
3059
|
+
messageId: added.userMessageId,
|
|
3060
|
+
token: added.conversationToken,
|
|
3061
|
+
answerId: null
|
|
3062
|
+
};
|
|
3063
|
+
showFollowUp(handle, { pending: "asking" });
|
|
3064
|
+
}).finally(() => {
|
|
3065
|
+
sendingRef.current = false;
|
|
3066
|
+
});
|
|
3067
|
+
},
|
|
3068
|
+
[attend, showFollowUp, tell]
|
|
3069
|
+
);
|
|
2350
3070
|
const ask = react.useCallback(
|
|
2351
3071
|
(text) => {
|
|
2352
3072
|
const conversation2 = conversationRef.current;
|
|
@@ -2355,6 +3075,11 @@ function AvatarLiveView({
|
|
|
2355
3075
|
return;
|
|
2356
3076
|
}
|
|
2357
3077
|
const spoken = !textBoxRef.current;
|
|
3078
|
+
const handle = sessionRef.current;
|
|
3079
|
+
if (spoken && handle !== null && handle.followUps !== null) {
|
|
3080
|
+
askFollowUp(handle, conversation2, question);
|
|
3081
|
+
return;
|
|
3082
|
+
}
|
|
2358
3083
|
if (spoken && conversation2.messages.length > 0) return;
|
|
2359
3084
|
sendingRef.current = true;
|
|
2360
3085
|
setAskedHere(null);
|
|
@@ -2376,8 +3101,64 @@ function AvatarLiveView({
|
|
|
2376
3101
|
sendingRef.current = false;
|
|
2377
3102
|
});
|
|
2378
3103
|
},
|
|
2379
|
-
[setAskedHere]
|
|
3104
|
+
[askFollowUp, setAskedHere]
|
|
2380
3105
|
);
|
|
3106
|
+
react.useEffect(() => {
|
|
3107
|
+
const handle = sessionRef.current;
|
|
3108
|
+
const run = handle?.followUps ?? null;
|
|
3109
|
+
if (handle === null || run === null || handle.done || run.asked === null) {
|
|
3110
|
+
return;
|
|
3111
|
+
}
|
|
3112
|
+
const asked = run.asked;
|
|
3113
|
+
const chat = conversationRef.current;
|
|
3114
|
+
const messages = chat?.messages ?? [];
|
|
3115
|
+
const at = messages.findIndex((message) => message.id === asked.messageId);
|
|
3116
|
+
const question = at === -1 ? void 0 : messages[at];
|
|
3117
|
+
const answer = at === -1 ? void 0 : messages[at + 1];
|
|
3118
|
+
const drop = (unread2) => {
|
|
3119
|
+
run.asked = null;
|
|
3120
|
+
showFollowUp(handle, { pending: null, unread: unread2 });
|
|
3121
|
+
attend();
|
|
3122
|
+
};
|
|
3123
|
+
const unread = (id, reason) => {
|
|
3124
|
+
if (id === null || run.sent.has(id) || !isAvatarMessageId(id)) {
|
|
3125
|
+
drop(reason);
|
|
3126
|
+
return;
|
|
3127
|
+
}
|
|
3128
|
+
run.asked = null;
|
|
3129
|
+
readFollowUp(handle, id, reason);
|
|
3130
|
+
};
|
|
3131
|
+
if (question === void 0 || (chat?.getConversationToken() ?? null) !== run.token) {
|
|
3132
|
+
drop(null);
|
|
3133
|
+
return;
|
|
3134
|
+
}
|
|
3135
|
+
if (answer === void 0) {
|
|
3136
|
+
if (!inFlight(chat)) unread(asked.answerId, "answer_failed");
|
|
3137
|
+
return;
|
|
3138
|
+
}
|
|
3139
|
+
if (answer.status === "PENDING" || answer.status === "RUNNING") {
|
|
3140
|
+
if (answer.role === "ASSISTANT") asked.answerId = answer.id;
|
|
3141
|
+
return;
|
|
3142
|
+
}
|
|
3143
|
+
if (answer.role !== "ASSISTANT") {
|
|
3144
|
+
unread(asked.answerId, "answer_failed");
|
|
3145
|
+
return;
|
|
3146
|
+
}
|
|
3147
|
+
if (answer.status !== "COMPLETED" || answer.content.trim() === "") {
|
|
3148
|
+
unread(answer.id, "answer_failed");
|
|
3149
|
+
return;
|
|
3150
|
+
}
|
|
3151
|
+
if (run.sent.has(answer.id) || !isAvatarMessageId(answer.id)) {
|
|
3152
|
+
drop("not_eligible");
|
|
3153
|
+
return;
|
|
3154
|
+
}
|
|
3155
|
+
if (at + 2 !== messages.length || !readable(question, answer)) {
|
|
3156
|
+
unread(answer.id, "not_eligible");
|
|
3157
|
+
return;
|
|
3158
|
+
}
|
|
3159
|
+
run.asked = null;
|
|
3160
|
+
readFollowUp(handle, answer.id, null);
|
|
3161
|
+
});
|
|
2381
3162
|
const newChatForAvatar = react.useCallback(() => {
|
|
2382
3163
|
const conversation2 = conversationRef.current;
|
|
2383
3164
|
if (conversation2 === null) return;
|
|
@@ -2401,7 +3182,14 @@ function AvatarLiveView({
|
|
|
2401
3182
|
const focused = document.activeElement;
|
|
2402
3183
|
if (focused && focused !== document.body && focused.isConnected) return;
|
|
2403
3184
|
rootRef.current?.querySelector(PRIMARY)?.focus();
|
|
2404
|
-
}, [
|
|
3185
|
+
}, [
|
|
3186
|
+
active,
|
|
3187
|
+
phase.name,
|
|
3188
|
+
askState,
|
|
3189
|
+
shown.pendingAnswerId,
|
|
3190
|
+
blocked,
|
|
3191
|
+
followUpAsks
|
|
3192
|
+
]);
|
|
2405
3193
|
const onPlaying = goLive;
|
|
2406
3194
|
const enablePlayback = react.useCallback(() => {
|
|
2407
3195
|
void sessionRef.current?.joined?.enablePlayback().catch(() => void 0);
|
|
@@ -2801,13 +3589,32 @@ function AvatarLiveView({
|
|
|
2801
3589
|
actions2 = stopButton;
|
|
2802
3590
|
break;
|
|
2803
3591
|
case "pending":
|
|
2804
|
-
case "live":
|
|
2805
|
-
status = phase.name === "live" ? speakingLine(phase.session) : S.connecting;
|
|
3592
|
+
case "live": {
|
|
2806
3593
|
top2 = badge;
|
|
2807
3594
|
media2 = liveMedia(phase.name === "live");
|
|
2808
3595
|
body2 = liveBody(phase.session);
|
|
2809
3596
|
actions2 = liveActions(readInChatButton(false));
|
|
3597
|
+
if (followUp === null) {
|
|
3598
|
+
status = phase.name === "live" ? speakingLine(phase.session) : S.connecting;
|
|
3599
|
+
break;
|
|
3600
|
+
}
|
|
3601
|
+
status = followUp.speaking ? speakingLine(phase.session) : followUp.pending !== null ? (followUp.pending === "asking" ? activity : null) ?? S.gettingAnswer : followUp.unread !== null ? S[FOLLOW_UP_TEXT[followUp.unread]] : followUp.closed ? S.followUpLast : S.followUpIdle;
|
|
3602
|
+
if (!followUp.speaking && followUp.pending === null && followUp.unread !== null && !followUp.closed) {
|
|
3603
|
+
body2 = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3604
|
+
body2,
|
|
3605
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3606
|
+
"p",
|
|
3607
|
+
{
|
|
3608
|
+
"data-boff-explore": "avatar-live-idle",
|
|
3609
|
+
className: "text-xs text-muted-foreground",
|
|
3610
|
+
children: S.followUpIdle
|
|
3611
|
+
}
|
|
3612
|
+
)
|
|
3613
|
+
] });
|
|
3614
|
+
}
|
|
3615
|
+
if (followUpAsks) box = "spoken";
|
|
2810
3616
|
break;
|
|
3617
|
+
}
|
|
2811
3618
|
case "ended":
|
|
2812
3619
|
status = S[phase.how];
|
|
2813
3620
|
outcome2 = true;
|
|
@@ -2855,7 +3662,7 @@ function AvatarLiveView({
|
|
|
2855
3662
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: S.newChatNote })
|
|
2856
3663
|
] }) : null,
|
|
2857
3664
|
chips,
|
|
2858
|
-
box !== null && usedToday ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3665
|
+
box !== null && usedToday && phase.name !== "live" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2859
3666
|
"p",
|
|
2860
3667
|
{
|
|
2861
3668
|
"data-boff-explore": "avatar-live-used-today",
|
|
@@ -3084,7 +3891,12 @@ function AskBox({
|
|
|
3084
3891
|
dir: "auto",
|
|
3085
3892
|
"data-avatar-primary": touchFirst ? void 0 : "",
|
|
3086
3893
|
"data-boff-explore": "avatar-live-ask-input",
|
|
3087
|
-
className:
|
|
3894
|
+
className: chunkC6OJIHNO_js.cn(
|
|
3895
|
+
// Kept out of a site's session replay before Send (rrweb's default classes; `rr-ignore` for its input
|
|
3896
|
+
// events): the text may be what the mic heard, or what the visitor typed.
|
|
3897
|
+
REPLAY_PRIVATE,
|
|
3898
|
+
"rr-ignore max-h-32 min-h-11 flex-1 resize-none rounded-md border border-border bg-background px-3 py-2.5 text-base text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring sm:text-sm"
|
|
3899
|
+
),
|
|
3088
3900
|
onChange: (event) => {
|
|
3089
3901
|
setRefusedFiles(false);
|
|
3090
3902
|
onChange(event.target.value);
|