@burdenoff/website-sdk 2026.1104.4 → 2026.1105.2
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 +67 -3
- 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 +1059 -45
- package/dist/avatar-live-view/index.js.map +1 -1
- package/dist/avatar-live-view/index.mjs +1054 -40
- package/dist/avatar-live-view/index.mjs.map +1 -1
- package/dist/avatar-mic/index.d.mts +193 -0
- package/dist/avatar-mic/index.d.ts +193 -0
- package/dist/avatar-mic/index.js +738 -0
- package/dist/avatar-mic/index.js.map +1 -0
- package/dist/avatar-mic/index.mjs +736 -0
- package/dist/avatar-mic/index.mjs.map +1 -0
- package/dist/{chunk-7MSB3EUI.mjs → chunk-MVNN6LLO.mjs} +4 -3
- package/dist/chunk-MVNN6LLO.mjs.map +1 -0
- package/dist/{chunk-WSEQ2I6I.js → chunk-XIZE37IB.js} +4 -3
- package/dist/chunk-XIZE37IB.js.map +1 -0
- package/dist/{explore-avatar-live-config-B6_p0q1c.d.mts → explore-avatar-live-config-BL8FTm0I.d.mts} +9 -1
- package/dist/{explore-avatar-live-config-DRpS-mqQ.d.ts → explore-avatar-live-config-auLKxSzx.d.ts} +9 -1
- package/dist/index.d.mts +9 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.js +9 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5 -3
- 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-7MSB3EUI.mjs.map +0 -1
- package/dist/chunk-WSEQ2I6I.js.map +0 -1
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkXIZE37IB_js = require('../chunk-XIZE37IB.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');
|
|
7
7
|
var react = require('react');
|
|
8
|
+
var lucideReact = require('lucide-react');
|
|
8
9
|
var jsxRuntime = require('react/jsx-runtime');
|
|
9
10
|
|
|
10
11
|
// src/components/explore-avatar-api.ts
|
|
11
12
|
var AVAILABILITY_TIMEOUT_MS = 8e3;
|
|
12
13
|
var CREATE_TIMEOUT_MS = 3e4;
|
|
14
|
+
var TURN_TIMEOUT_MS = 8e3;
|
|
15
|
+
var TURN_ANSWER_TIMEOUT_MS = 25e3;
|
|
13
16
|
var MESSAGE_ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
17
|
+
var STT_TOKEN = /^[A-Za-z0-9._~-]{1,4096}$/;
|
|
14
18
|
var CONVERSATION_TOKEN = /^[A-Za-z0-9._~+/=-]{16,512}$/;
|
|
15
19
|
var LOCALE = /^[A-Za-z0-9-]{1,16}$/;
|
|
16
20
|
var SESSION_ID = /^avs_[0-9a-f]{32}$/;
|
|
@@ -58,6 +62,15 @@ function readQuestions(value) {
|
|
|
58
62
|
if (typeof value !== "string" || !QUESTION_MODES.has(value)) return null;
|
|
59
63
|
return value === "any" ? "any" : "suggested";
|
|
60
64
|
}
|
|
65
|
+
function readSttOffer(value) {
|
|
66
|
+
if (!isRecord(value)) return void 0;
|
|
67
|
+
const keys = Object.keys(value).sort();
|
|
68
|
+
const { token, expiresAt } = value;
|
|
69
|
+
if (keys.length !== 2 || keys[0] !== "expiresAt" || keys[1] !== "token" || typeof token !== "string" || !STT_TOKEN.test(token) || typeof expiresAt !== "number" || !Number.isSafeInteger(expiresAt) || expiresAt <= 0) {
|
|
70
|
+
return void 0;
|
|
71
|
+
}
|
|
72
|
+
return { token, expiresAt };
|
|
73
|
+
}
|
|
61
74
|
function readAvailability(status, body) {
|
|
62
75
|
if (status !== 200 || !isRecord(body)) return INERT;
|
|
63
76
|
const { available, reason } = body;
|
|
@@ -68,12 +81,19 @@ function readAvailability(status, body) {
|
|
|
68
81
|
const { consentVersion, maxSessionSeconds } = body;
|
|
69
82
|
const botCheck = readBotCheck(body.botCheck);
|
|
70
83
|
const questions = readQuestions(body.questions);
|
|
71
|
-
if (available !== true || typeof consentVersion !== "string" || !
|
|
84
|
+
if (available !== true || typeof consentVersion !== "string" || !chunkXIZE37IB_js.isAvatarVersion(consentVersion) || typeof maxSessionSeconds !== "number" || !Number.isInteger(maxSessionSeconds) || maxSessionSeconds < 1 || botCheck === null || questions === null) {
|
|
72
85
|
return INERT;
|
|
73
86
|
}
|
|
87
|
+
const stt = readSttOffer(body.stt);
|
|
74
88
|
return {
|
|
75
89
|
kind: "available",
|
|
76
|
-
offer: {
|
|
90
|
+
offer: {
|
|
91
|
+
consentVersion,
|
|
92
|
+
maxSessionSeconds,
|
|
93
|
+
botCheck,
|
|
94
|
+
questions,
|
|
95
|
+
...stt === void 0 ? {} : { stt }
|
|
96
|
+
}
|
|
77
97
|
};
|
|
78
98
|
}
|
|
79
99
|
const meaning = available ? void 0 : AVAILABILITY_REASONS.get(reason);
|
|
@@ -97,6 +117,21 @@ function errorCode(body) {
|
|
|
97
117
|
const { code } = body.error;
|
|
98
118
|
return typeof code === "string" ? code : null;
|
|
99
119
|
}
|
|
120
|
+
var MAX_FOLLOW_UP_ANSWERS = 16;
|
|
121
|
+
var MAX_FOLLOW_UP_SECONDS = 600;
|
|
122
|
+
var FOLLOW_UP_KEYS = ["idleSeconds", "maxAnswerWaitSeconds", "maxAnswers"];
|
|
123
|
+
function isWholeBetween(value, max) {
|
|
124
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 1 && value <= max;
|
|
125
|
+
}
|
|
126
|
+
function readFollowUps(value) {
|
|
127
|
+
if (!isRecord(value)) return void 0;
|
|
128
|
+
const keys = Object.keys(value).sort();
|
|
129
|
+
const { maxAnswers, idleSeconds, maxAnswerWaitSeconds } = value;
|
|
130
|
+
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)) {
|
|
131
|
+
return void 0;
|
|
132
|
+
}
|
|
133
|
+
return { maxAnswers, idleSeconds, maxAnswerWaitSeconds };
|
|
134
|
+
}
|
|
100
135
|
function readCreated(body) {
|
|
101
136
|
if (!isRecord(body)) return FAILED;
|
|
102
137
|
const { sessionId, endSecret } = body;
|
|
@@ -114,6 +149,7 @@ function readCreated(body) {
|
|
|
114
149
|
if (typeof livekitUrl !== "string" || !livekitUrl.startsWith("wss://") || typeof participantToken !== "string" || participantToken === "" || typeof workerIdentity !== "string" || workerIdentity === "" || typeof locale !== "string" || typeof text !== "string") {
|
|
115
150
|
return unusable;
|
|
116
151
|
}
|
|
152
|
+
const followUps = readFollowUps(body.followUps);
|
|
117
153
|
return {
|
|
118
154
|
kind: "created",
|
|
119
155
|
session: {
|
|
@@ -122,7 +158,8 @@ function readCreated(body) {
|
|
|
122
158
|
endsAt,
|
|
123
159
|
join: { livekitUrl, participantToken, workerIdentity },
|
|
124
160
|
captions: { locale, text },
|
|
125
|
-
excerpt: body.excerpt === true
|
|
161
|
+
excerpt: body.excerpt === true,
|
|
162
|
+
...followUps === void 0 ? {} : { followUps }
|
|
126
163
|
}
|
|
127
164
|
};
|
|
128
165
|
}
|
|
@@ -161,7 +198,14 @@ function deadline(timeoutMs, outer) {
|
|
|
161
198
|
};
|
|
162
199
|
}
|
|
163
200
|
async function requestAvailability(target) {
|
|
164
|
-
|
|
201
|
+
try {
|
|
202
|
+
return await requestAvailabilityAnswer(target);
|
|
203
|
+
} catch {
|
|
204
|
+
return INERT;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async function requestAvailabilityAnswer(target) {
|
|
208
|
+
if (!chunkXIZE37IB_js.isAvatarSite(target.site) || !LOCALE.test(target.locale)) return INERT;
|
|
165
209
|
const query = new URLSearchParams({
|
|
166
210
|
site: target.site,
|
|
167
211
|
locale: target.locale
|
|
@@ -176,8 +220,6 @@ async function requestAvailability(target) {
|
|
|
176
220
|
signal: timer.signal
|
|
177
221
|
});
|
|
178
222
|
return readAvailability(response.status, await readJson(response));
|
|
179
|
-
} catch {
|
|
180
|
-
return INERT;
|
|
181
223
|
} finally {
|
|
182
224
|
timer.done();
|
|
183
225
|
}
|
|
@@ -248,6 +290,96 @@ function sendEnd(target) {
|
|
|
248
290
|
} catch {
|
|
249
291
|
}
|
|
250
292
|
}
|
|
293
|
+
var TURN_REFUSALS = /* @__PURE__ */ new Map([
|
|
294
|
+
["no_time", "no_time"],
|
|
295
|
+
["max_answers", "max_answers"],
|
|
296
|
+
["not_eligible", "not_eligible"],
|
|
297
|
+
["answer_claimed", "answer_claimed"],
|
|
298
|
+
["limits", "limits"],
|
|
299
|
+
["paused", "paused"],
|
|
300
|
+
["busy", "busy"]
|
|
301
|
+
]);
|
|
302
|
+
var MAX_TURN_RETRY_AFTER_SECONDS = 600;
|
|
303
|
+
function retryAfterOf(body) {
|
|
304
|
+
if (!isRecord(body) || !isRecord(body.error)) return void 0;
|
|
305
|
+
const { retryAfterSeconds } = body.error;
|
|
306
|
+
return isWholeBetween(retryAfterSeconds, MAX_TURN_RETRY_AFTER_SECONDS) ? retryAfterSeconds : void 0;
|
|
307
|
+
}
|
|
308
|
+
var TURN_KEY = /^[A-Za-z0-9_-]{8,64}$/;
|
|
309
|
+
var LOST = { kind: "failed", lost: true };
|
|
310
|
+
var TURN_FAILED = { kind: "failed", lost: false };
|
|
311
|
+
function readAccepted(body) {
|
|
312
|
+
if (!isRecord(body) || body.accepted !== true || !isRecord(body.captions)) {
|
|
313
|
+
return LOST;
|
|
314
|
+
}
|
|
315
|
+
const { locale, text } = body.captions;
|
|
316
|
+
if (typeof locale !== "string" || typeof text !== "string") return LOST;
|
|
317
|
+
return {
|
|
318
|
+
kind: "accepted",
|
|
319
|
+
captions: { locale, text },
|
|
320
|
+
excerpt: body.excerpt === true
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
function readTurn(kind, status, body) {
|
|
324
|
+
if (status === 200) {
|
|
325
|
+
if (kind === "answer") return readAccepted(body);
|
|
326
|
+
return isRecord(body) && body.ok === true ? { kind: "ok" } : LOST;
|
|
327
|
+
}
|
|
328
|
+
const code = errorCode(body);
|
|
329
|
+
const refusal = code === null ? void 0 : TURN_REFUSALS.get(code);
|
|
330
|
+
if (refusal !== void 0) {
|
|
331
|
+
const retryAfterSeconds = retryAfterOf(body);
|
|
332
|
+
return retryAfterSeconds === void 0 ? { kind: "refused", reason: refusal } : { kind: "refused", reason: refusal, retryAfterSeconds };
|
|
333
|
+
}
|
|
334
|
+
if (status === 404) return { kind: "gone" };
|
|
335
|
+
return status >= 500 ? LOST : TURN_FAILED;
|
|
336
|
+
}
|
|
337
|
+
function requestTurn(target) {
|
|
338
|
+
const { kind, messageId, idempotencyKey } = target;
|
|
339
|
+
const nonce = newIdempotencyKey();
|
|
340
|
+
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)) {
|
|
341
|
+
return Promise.resolve(TURN_FAILED);
|
|
342
|
+
}
|
|
343
|
+
const payload = JSON.stringify({
|
|
344
|
+
secret: target.secret,
|
|
345
|
+
kind,
|
|
346
|
+
nonce,
|
|
347
|
+
...messageId === void 0 ? {} : { messageId },
|
|
348
|
+
...idempotencyKey === void 0 ? {} : { idempotencyKey }
|
|
349
|
+
});
|
|
350
|
+
const controller = new AbortController();
|
|
351
|
+
const answer = (async () => {
|
|
352
|
+
try {
|
|
353
|
+
const response = await fetch(
|
|
354
|
+
`${target.base}/sessions/${encodeURIComponent(target.sessionId)}/turn`,
|
|
355
|
+
{
|
|
356
|
+
method: "POST",
|
|
357
|
+
credentials: "omit",
|
|
358
|
+
cache: "no-store",
|
|
359
|
+
headers: { ...target.headers },
|
|
360
|
+
body: payload,
|
|
361
|
+
signal: controller.signal
|
|
362
|
+
}
|
|
363
|
+
);
|
|
364
|
+
return readTurn(kind, response.status, await readJson(response));
|
|
365
|
+
} catch {
|
|
366
|
+
return LOST;
|
|
367
|
+
}
|
|
368
|
+
})();
|
|
369
|
+
return new Promise((resolve) => {
|
|
370
|
+
const timer = setTimeout(
|
|
371
|
+
() => {
|
|
372
|
+
controller.abort();
|
|
373
|
+
resolve(LOST);
|
|
374
|
+
},
|
|
375
|
+
kind === "answer" ? TURN_ANSWER_TIMEOUT_MS : TURN_TIMEOUT_MS
|
|
376
|
+
);
|
|
377
|
+
void answer.then((result) => {
|
|
378
|
+
clearTimeout(timer);
|
|
379
|
+
resolve(result);
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
}
|
|
251
383
|
var BOT_BINDING_PREFIX = "boff-web-avatar-cdata/v1";
|
|
252
384
|
function webCryptoSubtle() {
|
|
253
385
|
const subtle = globalThis.crypto?.subtle;
|
|
@@ -345,7 +477,14 @@ var EN = {
|
|
|
345
477
|
usedToday: "You've had today's avatar session. Answers will show here as text.",
|
|
346
478
|
needsAnswerAny: "Ask a question here, and the avatar reads the answer aloud.",
|
|
347
479
|
startNewChatDetailAny: "The avatar reads aloud only the first answer in a new chat started in the Avatar tab, provided it passes the speech checks.",
|
|
348
|
-
notEligibleAny: "The avatar can't read this answer aloud. The answer and sources are in Chat."
|
|
480
|
+
notEligibleAny: "The avatar can't read this answer aloud. The answer and sources are in Chat.",
|
|
481
|
+
followUpIdle: "The avatar ends after about thirty seconds without a question.",
|
|
482
|
+
followUpNoTime: "There isn't enough time left in this session to read that answer. It's in Chat.",
|
|
483
|
+
followUpMaxAnswers: "The avatar has read all the answers it can in this session. This one is in Chat.",
|
|
484
|
+
followUpLast: "That was the last answer the avatar can read in this session.",
|
|
485
|
+
followUpLimits: "The live avatar has reached its limit for now. This answer is in Chat.",
|
|
486
|
+
followUpPaused: "The live avatar is paused right now. This answer is in Chat.",
|
|
487
|
+
followUpUnread: "The avatar couldn't read that answer. It's in Chat."
|
|
349
488
|
};
|
|
350
489
|
var ZH = {
|
|
351
490
|
checking: "\u6B63\u5728\u68C0\u67E5\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u662F\u5426\u53EF\u7528\u2026",
|
|
@@ -406,7 +545,16 @@ var ZH = {
|
|
|
406
545
|
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",
|
|
407
546
|
needsAnswerAny: "\u5728\u8FD9\u91CC\u63D0\u95EE\uFF0C\u865A\u62DF\u5F62\u8C61\u4F1A\u6717\u8BFB\u56DE\u7B54\u3002",
|
|
408
547
|
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",
|
|
409
|
-
notEligibleAny: "\u865A\u62DF\u5F62\u8C61\u65E0\u6CD5\u6717\u8BFB\u6B64\u56DE\u7B54\u3002\u56DE\u7B54\u548C\u6765\u6E90\u5728\u804A\u5929\u4E2D\u3002"
|
|
548
|
+
notEligibleAny: "\u865A\u62DF\u5F62\u8C61\u65E0\u6CD5\u6717\u8BFB\u6B64\u56DE\u7B54\u3002\u56DE\u7B54\u548C\u6765\u6E90\u5728\u804A\u5929\u4E2D\u3002",
|
|
549
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft, the lines of a session that reads the answers after the first
|
|
550
|
+
// (Stage D).
|
|
551
|
+
followUpIdle: "\u5982\u679C\u7EA6\u4E09\u5341\u79D2\u5185\u6CA1\u6709\u65B0\u95EE\u9898\uFF0C\u865A\u62DF\u5F62\u8C61\u5C31\u4F1A\u7ED3\u675F\u3002",
|
|
552
|
+
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",
|
|
553
|
+
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",
|
|
554
|
+
followUpLast: "\u8FD9\u662F\u865A\u62DF\u5F62\u8C61\u5728\u672C\u6B21\u4F1A\u8BDD\u4E2D\u53EF\u6717\u8BFB\u7684\u6700\u540E\u4E00\u4E2A\u56DE\u7B54\u3002",
|
|
555
|
+
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",
|
|
556
|
+
followUpPaused: "\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u76EE\u524D\u5DF2\u6682\u505C\u3002\u6B64\u56DE\u7B54\u5728\u804A\u5929\u4E2D\u3002",
|
|
557
|
+
followUpUnread: "\u865A\u62DF\u5F62\u8C61\u65E0\u6CD5\u6717\u8BFB\u8BE5\u56DE\u7B54\u3002\u56DE\u7B54\u5728\u804A\u5929\u4E2D\u3002"
|
|
410
558
|
};
|
|
411
559
|
var HI = {
|
|
412
560
|
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",
|
|
@@ -467,7 +615,16 @@ var HI = {
|
|
|
467
615
|
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",
|
|
468
616
|
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",
|
|
469
617
|
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",
|
|
470
|
-
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"
|
|
618
|
+
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",
|
|
619
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft, the lines of a session that reads the answers after the first
|
|
620
|
+
// (Stage D).
|
|
621
|
+
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",
|
|
622
|
+
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",
|
|
623
|
+
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",
|
|
624
|
+
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",
|
|
625
|
+
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",
|
|
626
|
+
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",
|
|
627
|
+
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"
|
|
471
628
|
};
|
|
472
629
|
var ES = {
|
|
473
630
|
checking: "Comprobando si el avatar en vivo est\xE1 disponible\u2026",
|
|
@@ -528,7 +685,16 @@ var ES = {
|
|
|
528
685
|
usedToday: "Ya has usado la sesi\xF3n de avatar de hoy. Las respuestas se mostrar\xE1n aqu\xED como texto.",
|
|
529
686
|
needsAnswerAny: "Haz una pregunta aqu\xED y el avatar leer\xE1 la respuesta en voz alta.",
|
|
530
687
|
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.",
|
|
531
|
-
notEligibleAny: "El avatar no puede leer esta respuesta en voz alta. La respuesta y las fuentes est\xE1n en el chat."
|
|
688
|
+
notEligibleAny: "El avatar no puede leer esta respuesta en voz alta. La respuesta y las fuentes est\xE1n en el chat.",
|
|
689
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft, the lines of a session that reads the answers after the first
|
|
690
|
+
// (Stage D).
|
|
691
|
+
followUpIdle: "El avatar termina tras unos treinta segundos sin preguntas.",
|
|
692
|
+
followUpNoTime: "No queda tiempo suficiente en esta sesi\xF3n para leer esa respuesta. Est\xE1 en el chat.",
|
|
693
|
+
followUpMaxAnswers: "El avatar ya ha le\xEDdo todas las respuestas que puede en esta sesi\xF3n. Esta est\xE1 en el chat.",
|
|
694
|
+
followUpLast: "Esa fue la \xFAltima respuesta que el avatar puede leer en esta sesi\xF3n.",
|
|
695
|
+
followUpLimits: "El avatar en vivo ha alcanzado su l\xEDmite por ahora. Esta respuesta est\xE1 en el chat.",
|
|
696
|
+
followUpPaused: "El avatar en vivo est\xE1 en pausa ahora mismo. Esta respuesta est\xE1 en el chat.",
|
|
697
|
+
followUpUnread: "El avatar no pudo leer esa respuesta. Est\xE1 en el chat."
|
|
532
698
|
};
|
|
533
699
|
var AR = {
|
|
534
700
|
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",
|
|
@@ -589,7 +755,16 @@ var AR = {
|
|
|
589
755
|
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.",
|
|
590
756
|
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.",
|
|
591
757
|
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.",
|
|
592
|
-
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."
|
|
758
|
+
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.",
|
|
759
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft, the lines of a session that reads the answers after the first
|
|
760
|
+
// (Stage D).
|
|
761
|
+
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.",
|
|
762
|
+
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.",
|
|
763
|
+
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.",
|
|
764
|
+
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.",
|
|
765
|
+
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.",
|
|
766
|
+
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.",
|
|
767
|
+
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."
|
|
593
768
|
};
|
|
594
769
|
var TA = {
|
|
595
770
|
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",
|
|
@@ -650,7 +825,16 @@ var TA = {
|
|
|
650
825
|
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.",
|
|
651
826
|
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.",
|
|
652
827
|
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.",
|
|
653
|
-
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."
|
|
828
|
+
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.",
|
|
829
|
+
// TRANSLATION REVIEW PENDING (BOFF-9797): draft, the lines of a session that reads the answers after the first
|
|
830
|
+
// (Stage D).
|
|
831
|
+
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.",
|
|
832
|
+
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.",
|
|
833
|
+
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.",
|
|
834
|
+
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.",
|
|
835
|
+
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.",
|
|
836
|
+
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.",
|
|
837
|
+
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."
|
|
654
838
|
};
|
|
655
839
|
var STRINGS = {
|
|
656
840
|
en: EN,
|
|
@@ -800,12 +984,378 @@ var CONSENT = /* @__PURE__ */ new Map([
|
|
|
800
984
|
questions: "any"
|
|
801
985
|
}
|
|
802
986
|
}
|
|
987
|
+
],
|
|
988
|
+
[
|
|
989
|
+
// Stage D (BOFF-9797), a DRAFT for the operator's review that no environment uses yet (global-speech-svc reserves the
|
|
990
|
+
// same two versions): web-consent-2026-11-12-b with only its intro changed. The avatar reads the answer to each
|
|
991
|
+
// question asked in the Avatar tab, in one session, waits about thirty seconds after an answer for the next question,
|
|
992
|
+
// then ends; the session lasts at most three minutes, and an answer that does not fit the time left is read in part
|
|
993
|
+
// (and said so) or not read, and stays in Chat. The notice files of record and their digests are a gitops change of
|
|
994
|
+
// their own, made with the review.
|
|
995
|
+
"web-consent-2026-11-19-d",
|
|
996
|
+
{
|
|
997
|
+
en: {
|
|
998
|
+
title: "Live AI avatar (beta)",
|
|
999
|
+
introProduct: null,
|
|
1000
|
+
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.",
|
|
1001
|
+
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.",
|
|
1002
|
+
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.",
|
|
1003
|
+
privacyLink: "How we handle this",
|
|
1004
|
+
botCheck: "turnstile",
|
|
1005
|
+
questions: "any",
|
|
1006
|
+
followUps: true
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
],
|
|
1010
|
+
[
|
|
1011
|
+
// The words of web-consent-2026-11-19-d without "When you start, Cloudflare checks for bots.", for a server that runs
|
|
1012
|
+
// no bot check. A DRAFT, like it.
|
|
1013
|
+
"web-consent-2026-11-20-d",
|
|
1014
|
+
{
|
|
1015
|
+
en: {
|
|
1016
|
+
title: "Live AI avatar (beta)",
|
|
1017
|
+
introProduct: null,
|
|
1018
|
+
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.",
|
|
1019
|
+
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.",
|
|
1020
|
+
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.",
|
|
1021
|
+
privacyLink: "How we handle this",
|
|
1022
|
+
botCheck: "off",
|
|
1023
|
+
questions: "any",
|
|
1024
|
+
followUps: true
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
803
1027
|
]
|
|
804
1028
|
]);
|
|
805
1029
|
function avatarConsentCopy(version, locale) {
|
|
806
|
-
if (!
|
|
1030
|
+
if (!chunkXIZE37IB_js.isAvatarLiveLocaleReviewed(locale)) return null;
|
|
807
1031
|
return CONSENT.get(version)?.[locale] ?? null;
|
|
808
1032
|
}
|
|
1033
|
+
var MIC_EN = {
|
|
1034
|
+
micStart: "Ask by voice",
|
|
1035
|
+
micStop: "Stop recording",
|
|
1036
|
+
micAllow: "Use the microphone",
|
|
1037
|
+
micNotNow: "Not now",
|
|
1038
|
+
micStarting: "Starting the microphone\u2026",
|
|
1039
|
+
micListening: "Listening\u2026",
|
|
1040
|
+
micElapsed: "{elapsed} of {limit}",
|
|
1041
|
+
micFinishing: "Getting the text\u2026",
|
|
1042
|
+
micDone: "Check the text, then press Send.",
|
|
1043
|
+
micDoneLimit: "The recording stopped at thirty seconds. Check the text, then press Send.",
|
|
1044
|
+
micNoSpeech: "No words were heard. Try again, or type your question.",
|
|
1045
|
+
micDenied: "The microphone is blocked in this browser. You can type your question instead.",
|
|
1046
|
+
micUnsupported: "The microphone can't be used here. You can type your question instead.",
|
|
1047
|
+
micUnavailable: "Voice questions aren't available right now. You can type your question instead.",
|
|
1048
|
+
micLimit: "Voice questions have reached their limit for now. You can type your question instead.",
|
|
1049
|
+
micBusy: "Voice questions are busy right now. Try again in a moment, or type your question.",
|
|
1050
|
+
micFailed: "The microphone stopped before the text came. Try again, or type your question."
|
|
1051
|
+
};
|
|
1052
|
+
var MIC_STRINGS = { en: MIC_EN };
|
|
1053
|
+
function avatarMicStrings(locale) {
|
|
1054
|
+
if (!chunkXIZE37IB_js.isAvatarLiveLocaleReviewed(locale)) return null;
|
|
1055
|
+
return MIC_STRINGS[locale] ?? null;
|
|
1056
|
+
}
|
|
1057
|
+
var AVATAR_MIC_NOTICE_VERSION = "web-mic-2026-11-05";
|
|
1058
|
+
var MIC_NOTICE = /* @__PURE__ */ new Map([
|
|
1059
|
+
[
|
|
1060
|
+
AVATAR_MIC_NOTICE_VERSION,
|
|
1061
|
+
{
|
|
1062
|
+
en: {
|
|
1063
|
+
title: "Ask with your voice",
|
|
1064
|
+
processing: "While you speak, your voice is streamed to our speech service, and Google Cloud Speech-to-Text turns it into text in Google's EU region.",
|
|
1065
|
+
storage: "We don't store or log your audio, or the text made from it.",
|
|
1066
|
+
text: "The text appears in the box and stays in this browser until you press Send. Nothing is sent by itself. Once you send it, it is a chat message, kept like any other.",
|
|
1067
|
+
limits: "Each recording stops after thirty seconds, and voice questions have daily limits.",
|
|
1068
|
+
privacyLink: "How we handle this"
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
]
|
|
1072
|
+
]);
|
|
1073
|
+
function avatarMicNotice(version, locale) {
|
|
1074
|
+
if (!chunkXIZE37IB_js.isAvatarLiveLocaleReviewed(locale)) return null;
|
|
1075
|
+
return MIC_NOTICE.get(version)?.[locale] ?? null;
|
|
1076
|
+
}
|
|
1077
|
+
var PRIVACY_HREF = "/privacy#live-avatar";
|
|
1078
|
+
var LIMIT_SECONDS = 30;
|
|
1079
|
+
var TICK_MS = 250;
|
|
1080
|
+
var IDLE = { name: "idle" };
|
|
1081
|
+
var FOR_GOOD = /* @__PURE__ */ new Set([
|
|
1082
|
+
"unavailable",
|
|
1083
|
+
"limit"
|
|
1084
|
+
]);
|
|
1085
|
+
var FAILURE_TEXT = {
|
|
1086
|
+
denied: "micDenied",
|
|
1087
|
+
unsupported: "micUnsupported",
|
|
1088
|
+
unavailable: "micUnavailable",
|
|
1089
|
+
limit: "micLimit",
|
|
1090
|
+
busy: "micBusy",
|
|
1091
|
+
failed: "micFailed"
|
|
1092
|
+
};
|
|
1093
|
+
function loadAvatarMic() {
|
|
1094
|
+
return import('../avatar-mic/index.js').then((loaded) => ({
|
|
1095
|
+
createAvatarMic: loaded.createAvatarMic
|
|
1096
|
+
}));
|
|
1097
|
+
}
|
|
1098
|
+
function canUseAvatarMic() {
|
|
1099
|
+
return typeof navigator !== "undefined" && typeof navigator.mediaDevices?.getUserMedia === "function" && typeof AudioContext !== "undefined" && typeof AudioWorkletNode !== "undefined" && typeof WebSocket !== "undefined";
|
|
1100
|
+
}
|
|
1101
|
+
function clock(seconds) {
|
|
1102
|
+
const whole = Math.max(0, Math.floor(seconds));
|
|
1103
|
+
return `${Math.floor(whole / 60)}:${String(whole % 60).padStart(2, "0")}`;
|
|
1104
|
+
}
|
|
1105
|
+
function withHeardText(current, heard, maxLength) {
|
|
1106
|
+
const text = heard.trim();
|
|
1107
|
+
if (text === "") return current;
|
|
1108
|
+
const joined = current.trim() === "" ? text : `${current.replace(/\s+$/, "")} ${text}`;
|
|
1109
|
+
return joined.slice(0, Math.max(0, maxLength));
|
|
1110
|
+
}
|
|
1111
|
+
function useAvatarMic({
|
|
1112
|
+
enabled,
|
|
1113
|
+
locale,
|
|
1114
|
+
base,
|
|
1115
|
+
offer,
|
|
1116
|
+
refreshOffer,
|
|
1117
|
+
onText,
|
|
1118
|
+
onStart
|
|
1119
|
+
}) {
|
|
1120
|
+
const S = avatarMicStrings(locale);
|
|
1121
|
+
const copy = avatarMicNotice(AVATAR_MIC_NOTICE_VERSION, locale);
|
|
1122
|
+
const uid = react.useId();
|
|
1123
|
+
const noticeTitleId = `${uid}-mic-notice`;
|
|
1124
|
+
const [withdrawn, setWithdrawn] = react.useState(null);
|
|
1125
|
+
const [supported, setSupported] = react.useState(false);
|
|
1126
|
+
react.useEffect(() => setSupported(canUseAvatarMic()), []);
|
|
1127
|
+
const possible = enabled && supported && base !== null && S !== null && copy !== null;
|
|
1128
|
+
const offered = possible && withdrawn === null;
|
|
1129
|
+
const [state, setState] = react.useState(IDLE);
|
|
1130
|
+
const [noticeOpen, setNoticeOpen] = react.useState(false);
|
|
1131
|
+
const agreedRef = react.useRef(false);
|
|
1132
|
+
const [loading2, setLoading] = react.useState(false);
|
|
1133
|
+
const [loadFailed, setLoadFailed] = react.useState(false);
|
|
1134
|
+
const [now, setNow] = react.useState(() => Date.now());
|
|
1135
|
+
const micRef = react.useRef(null);
|
|
1136
|
+
const seqRef = react.useRef(0);
|
|
1137
|
+
const buttonRef = react.useRef(null);
|
|
1138
|
+
const allowRef = react.useRef(null);
|
|
1139
|
+
const usedEpochRef = react.useRef(0);
|
|
1140
|
+
const latest = react.useRef({ offer, refreshOffer, onText, onStart, base });
|
|
1141
|
+
latest.current = { offer, refreshOffer, onText, onStart, base };
|
|
1142
|
+
const cancel = react.useCallback(() => {
|
|
1143
|
+
seqRef.current += 1;
|
|
1144
|
+
setLoading(false);
|
|
1145
|
+
setLoadFailed(false);
|
|
1146
|
+
setNoticeOpen(false);
|
|
1147
|
+
micRef.current?.mic.cancel();
|
|
1148
|
+
}, []);
|
|
1149
|
+
react.useEffect(() => {
|
|
1150
|
+
if (!offered) cancel();
|
|
1151
|
+
}, [offered, cancel]);
|
|
1152
|
+
react.useEffect(
|
|
1153
|
+
() => () => {
|
|
1154
|
+
seqRef.current += 1;
|
|
1155
|
+
micRef.current?.mic.cancel();
|
|
1156
|
+
},
|
|
1157
|
+
[]
|
|
1158
|
+
);
|
|
1159
|
+
const micFor = react.useCallback(async (target) => {
|
|
1160
|
+
const held = micRef.current;
|
|
1161
|
+
if (held !== null && held.base === target) return held.mic;
|
|
1162
|
+
held?.mic.cancel();
|
|
1163
|
+
const loaded = await loadAvatarMic();
|
|
1164
|
+
const mic = loaded.createAvatarMic({
|
|
1165
|
+
base: target,
|
|
1166
|
+
notice: AVATAR_MIC_NOTICE_VERSION,
|
|
1167
|
+
offer: () => latest.current.offer,
|
|
1168
|
+
refreshOffer: () => latest.current.refreshOffer(),
|
|
1169
|
+
onState: setState
|
|
1170
|
+
});
|
|
1171
|
+
micRef.current = { base: target, mic };
|
|
1172
|
+
return mic;
|
|
1173
|
+
}, []);
|
|
1174
|
+
const press = react.useCallback(() => {
|
|
1175
|
+
const target = latest.current.base;
|
|
1176
|
+
if (target === null) return;
|
|
1177
|
+
seqRef.current += 1;
|
|
1178
|
+
const seq = seqRef.current;
|
|
1179
|
+
setLoadFailed(false);
|
|
1180
|
+
setLoading(true);
|
|
1181
|
+
void micFor(target).then(
|
|
1182
|
+
(mic) => {
|
|
1183
|
+
if (seqRef.current !== seq) return;
|
|
1184
|
+
setLoading(false);
|
|
1185
|
+
mic.start();
|
|
1186
|
+
},
|
|
1187
|
+
() => {
|
|
1188
|
+
if (seqRef.current !== seq) return;
|
|
1189
|
+
setLoading(false);
|
|
1190
|
+
setLoadFailed(true);
|
|
1191
|
+
}
|
|
1192
|
+
);
|
|
1193
|
+
}, [micFor]);
|
|
1194
|
+
const recording = loading2 || state.name === "starting" || state.name === "listening";
|
|
1195
|
+
const tap = react.useCallback(() => {
|
|
1196
|
+
if (state.name === "listening" || state.name === "starting") {
|
|
1197
|
+
micRef.current?.mic.stop();
|
|
1198
|
+
return;
|
|
1199
|
+
}
|
|
1200
|
+
if (loading2) {
|
|
1201
|
+
cancel();
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
latest.current.onStart?.();
|
|
1205
|
+
if (!agreedRef.current) {
|
|
1206
|
+
setNoticeOpen(true);
|
|
1207
|
+
void loadAvatarMic().catch(() => void 0);
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
press();
|
|
1211
|
+
}, [cancel, loading2, press, state.name]);
|
|
1212
|
+
const allow = react.useCallback(() => {
|
|
1213
|
+
agreedRef.current = true;
|
|
1214
|
+
setNoticeOpen(false);
|
|
1215
|
+
buttonRef.current?.focus();
|
|
1216
|
+
press();
|
|
1217
|
+
}, [press]);
|
|
1218
|
+
const notNow = react.useCallback(() => {
|
|
1219
|
+
setNoticeOpen(false);
|
|
1220
|
+
buttonRef.current?.focus();
|
|
1221
|
+
}, []);
|
|
1222
|
+
react.useEffect(() => {
|
|
1223
|
+
if (noticeOpen) allowRef.current?.focus();
|
|
1224
|
+
}, [noticeOpen]);
|
|
1225
|
+
const listeningSince = state.name === "listening" ? state.startedAt : null;
|
|
1226
|
+
react.useEffect(() => {
|
|
1227
|
+
if (listeningSince === null) return;
|
|
1228
|
+
setNow(Date.now());
|
|
1229
|
+
const timer = setInterval(() => setNow(Date.now()), TICK_MS);
|
|
1230
|
+
return () => clearInterval(timer);
|
|
1231
|
+
}, [listeningSince]);
|
|
1232
|
+
react.useEffect(() => {
|
|
1233
|
+
if (state.name === "done" && state.epoch !== usedEpochRef.current) {
|
|
1234
|
+
usedEpochRef.current = state.epoch;
|
|
1235
|
+
if (state.text !== "") latest.current.onText(state.text);
|
|
1236
|
+
} else if (state.name === "failed" && FOR_GOOD.has(state.reason)) {
|
|
1237
|
+
setWithdrawn(state.reason);
|
|
1238
|
+
}
|
|
1239
|
+
}, [state]);
|
|
1240
|
+
if (!possible || S === null || copy === null) {
|
|
1241
|
+
return {
|
|
1242
|
+
button: null,
|
|
1243
|
+
notice: null,
|
|
1244
|
+
lines: null,
|
|
1245
|
+
cancel,
|
|
1246
|
+
recording: false
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
const status = withdrawn !== null ? S[FAILURE_TEXT[withdrawn]] : loading2 ? S.micStarting : loadFailed ? S.micFailed : state.name === "starting" ? S.micStarting : state.name === "listening" ? S.micListening : state.name === "finishing" ? S.micFinishing : state.name === "done" ? state.text === "" ? S.micNoSpeech : state.limit ? S.micDoneLimit : S.micDone : state.name === "failed" ? S[FAILURE_TEXT[state.reason]] : "";
|
|
1250
|
+
const heard = offered && (state.name === "listening" || state.name === "finishing") ? state.text : "";
|
|
1251
|
+
const button = !offered ? null : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1252
|
+
"button",
|
|
1253
|
+
{
|
|
1254
|
+
ref: buttonRef,
|
|
1255
|
+
type: "button",
|
|
1256
|
+
"aria-label": recording ? S.micStop : S.micStart,
|
|
1257
|
+
"aria-pressed": recording,
|
|
1258
|
+
title: recording ? S.micStop : S.micStart,
|
|
1259
|
+
"data-boff-explore": "avatar-live-mic",
|
|
1260
|
+
"data-mic": loading2 ? "starting" : state.name,
|
|
1261
|
+
className: chunkC6OJIHNO_js.cn(
|
|
1262
|
+
"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",
|
|
1263
|
+
recording ? "border-primary bg-primary text-primary-foreground" : "border-border bg-background text-foreground hover:bg-accent hover:text-accent-foreground"
|
|
1264
|
+
),
|
|
1265
|
+
onClick: tap,
|
|
1266
|
+
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" })
|
|
1267
|
+
}
|
|
1268
|
+
);
|
|
1269
|
+
const notice = offered && noticeOpen ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1270
|
+
"section",
|
|
1271
|
+
{
|
|
1272
|
+
"aria-labelledby": noticeTitleId,
|
|
1273
|
+
"data-boff-explore": "avatar-live-mic-notice",
|
|
1274
|
+
className: "w-full rounded-lg border border-border p-3 text-start",
|
|
1275
|
+
children: [
|
|
1276
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { id: noticeTitleId, className: "text-sm font-semibold", children: copy.title }),
|
|
1277
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: copy.processing }),
|
|
1278
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: copy.storage }),
|
|
1279
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: copy.text }),
|
|
1280
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: copy.limits }),
|
|
1281
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1282
|
+
"a",
|
|
1283
|
+
{
|
|
1284
|
+
href: PRIVACY_HREF,
|
|
1285
|
+
target: "_blank",
|
|
1286
|
+
rel: "noopener noreferrer",
|
|
1287
|
+
"data-boff-explore": "avatar-live-mic-privacy",
|
|
1288
|
+
className: "mt-1 inline-block text-xs font-medium text-primary underline underline-offset-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
1289
|
+
children: copy.privacyLink
|
|
1290
|
+
}
|
|
1291
|
+
),
|
|
1292
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex flex-wrap gap-2", children: [
|
|
1293
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1294
|
+
"button",
|
|
1295
|
+
{
|
|
1296
|
+
ref: allowRef,
|
|
1297
|
+
type: "button",
|
|
1298
|
+
"data-boff-explore": "avatar-live-mic-allow",
|
|
1299
|
+
className: "inline-flex min-h-11 items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background motion-reduce:transition-none",
|
|
1300
|
+
onClick: allow,
|
|
1301
|
+
children: S.micAllow
|
|
1302
|
+
}
|
|
1303
|
+
),
|
|
1304
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1305
|
+
"button",
|
|
1306
|
+
{
|
|
1307
|
+
type: "button",
|
|
1308
|
+
"data-boff-explore": "avatar-live-mic-not-now",
|
|
1309
|
+
className: "inline-flex min-h-11 items-center justify-center rounded-md border border-border bg-background px-4 py-2 text-sm font-medium text-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background motion-reduce:transition-none",
|
|
1310
|
+
onClick: notNow,
|
|
1311
|
+
children: S.micNotNow
|
|
1312
|
+
}
|
|
1313
|
+
)
|
|
1314
|
+
] })
|
|
1315
|
+
]
|
|
1316
|
+
}
|
|
1317
|
+
) : null;
|
|
1318
|
+
const listening = offered && state.name === "listening";
|
|
1319
|
+
const lines = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1320
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
|
|
1321
|
+
listening ? /* @__PURE__ */ jsxRuntime.jsxs("span", { "aria-hidden": "true", className: "relative flex h-2 w-2 shrink-0", children: [
|
|
1322
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-primary opacity-75 motion-reduce:animate-none" }),
|
|
1323
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative inline-flex h-2 w-2 rounded-full bg-primary" })
|
|
1324
|
+
] }) : null,
|
|
1325
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { role: "status", "data-boff-explore": "avatar-live-mic-status", children: status }),
|
|
1326
|
+
listening ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1327
|
+
"span",
|
|
1328
|
+
{
|
|
1329
|
+
role: "timer",
|
|
1330
|
+
"data-boff-explore": "avatar-live-mic-elapsed",
|
|
1331
|
+
className: "tabular-nums",
|
|
1332
|
+
children: chunkN7E6Z2GF_js.fill(S.micElapsed, {
|
|
1333
|
+
elapsed: clock(
|
|
1334
|
+
Math.min(LIMIT_SECONDS, (now - (listeningSince ?? now)) / 1e3)
|
|
1335
|
+
),
|
|
1336
|
+
limit: clock(LIMIT_SECONDS)
|
|
1337
|
+
})
|
|
1338
|
+
}
|
|
1339
|
+
) : null
|
|
1340
|
+
] }),
|
|
1341
|
+
heard === "" ? null : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1342
|
+
"p",
|
|
1343
|
+
{
|
|
1344
|
+
"data-boff-explore": "avatar-live-mic-heard",
|
|
1345
|
+
dir: "auto",
|
|
1346
|
+
className: "line-clamp-3 break-words text-sm italic text-muted-foreground",
|
|
1347
|
+
children: heard
|
|
1348
|
+
}
|
|
1349
|
+
)
|
|
1350
|
+
] });
|
|
1351
|
+
return {
|
|
1352
|
+
button,
|
|
1353
|
+
notice,
|
|
1354
|
+
lines,
|
|
1355
|
+
cancel,
|
|
1356
|
+
recording: offered && state.name === "listening"
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
809
1359
|
|
|
810
1360
|
// src/components/explore-avatar-turnstile.ts
|
|
811
1361
|
var SCRIPT_SRC = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
|
|
@@ -921,12 +1471,18 @@ function runTurnstile(container, siteKey, action, cData) {
|
|
|
921
1471
|
}
|
|
922
1472
|
};
|
|
923
1473
|
}
|
|
924
|
-
var
|
|
1474
|
+
var PRIVACY_HREF2 = "/privacy#live-avatar";
|
|
925
1475
|
var SPEAKER_WAIT_MS = 3e4;
|
|
926
1476
|
var END_GRACE_MS = 1e4;
|
|
927
1477
|
var CLOCK_TOLERANCE_MS = 5e3;
|
|
928
1478
|
var UNHEARD_GRACE_MS = 5e3;
|
|
929
1479
|
var PLAYOUT_TAIL_MS = 5e3;
|
|
1480
|
+
var QUIET_PLAYED_OUT_MS = 2500;
|
|
1481
|
+
var OWED_VOICE_WAIT_MS = 15e3;
|
|
1482
|
+
var LISTENING_MARK_MS = 1e4;
|
|
1483
|
+
var BUSY_RETRY_MS = 2e3;
|
|
1484
|
+
var BUSY_RETRY_MAX_MS = 8e3;
|
|
1485
|
+
var MAX_BUSY_RETRIES = 24;
|
|
930
1486
|
var SERVICE_MAX_AGE_MS = 15 * 6e4;
|
|
931
1487
|
var ANSWER_OFFER_MS = SERVICE_MAX_AGE_MS - 6e4;
|
|
932
1488
|
var MAX_TIMER_MS = 2147483647;
|
|
@@ -934,13 +1490,19 @@ var ASK_MAX_LENGTH = 300;
|
|
|
934
1490
|
var MAX_REFERENCES = 64;
|
|
935
1491
|
var USED_ON_KEY = "boff.explore.avatarUsedOn";
|
|
936
1492
|
var PRIMARY = '[data-avatar-primary], [data-boff-explore="panel-continue-chat"]';
|
|
937
|
-
var
|
|
1493
|
+
var IDLE2 = { name: "idle" };
|
|
938
1494
|
var INERT2 = { name: "inert" };
|
|
939
1495
|
function stopAttending(handle) {
|
|
940
1496
|
if (handle.unheard !== null) clearTimeout(handle.unheard);
|
|
941
1497
|
if (handle.tail !== null) clearTimeout(handle.tail);
|
|
942
1498
|
handle.unheard = null;
|
|
943
1499
|
handle.tail = null;
|
|
1500
|
+
const run = handle.followUps;
|
|
1501
|
+
if (run === null) return;
|
|
1502
|
+
if (run.quiet !== null) clearTimeout(run.quiet);
|
|
1503
|
+
if (run.retry !== null) clearTimeout(run.retry);
|
|
1504
|
+
run.quiet = null;
|
|
1505
|
+
run.retry = null;
|
|
944
1506
|
}
|
|
945
1507
|
var REFUSAL_TEXT = {
|
|
946
1508
|
busy: "busy",
|
|
@@ -951,6 +1513,18 @@ var REFUSAL_TEXT = {
|
|
|
951
1513
|
not_eligible: "notEligible",
|
|
952
1514
|
answer_claimed: "answerClaimed"
|
|
953
1515
|
};
|
|
1516
|
+
var FOLLOW_UP_TEXT = {
|
|
1517
|
+
no_time: "followUpNoTime",
|
|
1518
|
+
max_answers: "followUpMaxAnswers",
|
|
1519
|
+
// The same line as a first answer the checks refuse: it stays in Chat as text.
|
|
1520
|
+
not_eligible: "notSpoken",
|
|
1521
|
+
answer_claimed: "answerClaimed",
|
|
1522
|
+
limits: "followUpLimits",
|
|
1523
|
+
paused: "followUpPaused",
|
|
1524
|
+
busy: "followUpUnread",
|
|
1525
|
+
answer_failed: "answerFailed",
|
|
1526
|
+
failed: "followUpUnread"
|
|
1527
|
+
};
|
|
954
1528
|
var EMPTY = {
|
|
955
1529
|
kind: "empty",
|
|
956
1530
|
answerId: null,
|
|
@@ -1024,6 +1598,16 @@ function firstExchange(messages, rules = SUGGESTED_RULES) {
|
|
|
1024
1598
|
function inFlight(conversation) {
|
|
1025
1599
|
return conversation?.phase === "sending" || conversation?.phase === "streaming";
|
|
1026
1600
|
}
|
|
1601
|
+
function acceptsQuestion(slot) {
|
|
1602
|
+
if (!slot.any || slot.textMode || slot.reason !== null) return false;
|
|
1603
|
+
return slot.phase === "needs-answer" ? slot.kind === "empty" : slot.phase === "live" && slot.followUp;
|
|
1604
|
+
}
|
|
1605
|
+
function followUpBox(phase, run, token) {
|
|
1606
|
+
return phase.name === "live" && run !== null && phase.session.followUp !== null && !phase.session.followUp.closed && token === run.token;
|
|
1607
|
+
}
|
|
1608
|
+
function micOfferOf(result) {
|
|
1609
|
+
return result.kind === "available" && result.offer.stt !== void 0 ? { ...result.offer.stt, receivedAt: Date.now() } : null;
|
|
1610
|
+
}
|
|
1027
1611
|
function utcToday() {
|
|
1028
1612
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1029
1613
|
}
|
|
@@ -1093,7 +1677,7 @@ async function loadModule(load) {
|
|
|
1093
1677
|
}
|
|
1094
1678
|
return loaded;
|
|
1095
1679
|
}
|
|
1096
|
-
function
|
|
1680
|
+
function clock2(seconds) {
|
|
1097
1681
|
const whole = Math.max(0, Math.ceil(seconds));
|
|
1098
1682
|
return `${Math.floor(whole / 60)}:${String(whole % 60).padStart(2, "0")}`;
|
|
1099
1683
|
}
|
|
@@ -1146,7 +1730,7 @@ function AvatarLiveView({
|
|
|
1146
1730
|
const shown = offeredAt(exchange, Date.now());
|
|
1147
1731
|
const { answerId, startOver } = shown;
|
|
1148
1732
|
const offerUntil = answerId === null ? null : exchange.offerUntil;
|
|
1149
|
-
const [phase, setPhase] = react.useState(
|
|
1733
|
+
const [phase, setPhase] = react.useState(IDLE2);
|
|
1150
1734
|
const [blocked, setBlocked] = react.useState(false);
|
|
1151
1735
|
const [now, setNow] = react.useState(() => Date.now());
|
|
1152
1736
|
const [draft, setDraft] = react.useState("");
|
|
@@ -1156,7 +1740,7 @@ function AvatarLiveView({
|
|
|
1156
1740
|
const [usedToday, setUsedToday] = react.useState(false);
|
|
1157
1741
|
const showing = active && !off;
|
|
1158
1742
|
const behindChat = open && !active && !off;
|
|
1159
|
-
const phaseRef = react.useRef(
|
|
1743
|
+
const phaseRef = react.useRef(IDLE2);
|
|
1160
1744
|
const latest = react.useRef({
|
|
1161
1745
|
live,
|
|
1162
1746
|
locale,
|
|
@@ -1172,6 +1756,9 @@ function AvatarLiveView({
|
|
|
1172
1756
|
const waitRef = react.useRef(null);
|
|
1173
1757
|
const sendingRef = react.useRef(false);
|
|
1174
1758
|
const textBoxRef = react.useRef(false);
|
|
1759
|
+
const [micOffer, setMicOffer] = react.useState(null);
|
|
1760
|
+
const micCancelRef = react.useRef(() => void 0);
|
|
1761
|
+
const sendDueRef = react.useRef(() => void 0);
|
|
1175
1762
|
const checkRef = react.useRef(null);
|
|
1176
1763
|
const askedLocale = react.useRef(locale);
|
|
1177
1764
|
const startRef = react.useRef(null);
|
|
@@ -1260,10 +1847,13 @@ function AvatarLiveView({
|
|
|
1260
1847
|
if (!handle || handle.done || latest.current.live.endUnattended !== true) {
|
|
1261
1848
|
return;
|
|
1262
1849
|
}
|
|
1263
|
-
const
|
|
1264
|
-
if (
|
|
1850
|
+
const shown2 = phaseRef.current;
|
|
1851
|
+
if (shown2.name !== "pending" && shown2.name !== "live") return;
|
|
1852
|
+
const current = shown2.name;
|
|
1265
1853
|
const away = !inSight();
|
|
1266
|
-
|
|
1854
|
+
const followUp2 = shown2.session.followUp;
|
|
1855
|
+
const asking = followUp2 !== null && followUp2.pending !== null;
|
|
1856
|
+
if (away && handle.playedOut && current === "live" && !asking) {
|
|
1267
1857
|
finish("finished", "user");
|
|
1268
1858
|
return;
|
|
1269
1859
|
}
|
|
@@ -1282,6 +1872,184 @@ function AvatarLiveView({
|
|
|
1282
1872
|
}
|
|
1283
1873
|
}, UNHEARD_GRACE_MS);
|
|
1284
1874
|
}, [finish, inSight]);
|
|
1875
|
+
const showFollowUp = react.useCallback(
|
|
1876
|
+
(handle, next, spoken) => {
|
|
1877
|
+
const current = phaseRef.current;
|
|
1878
|
+
if (sessionRef.current !== handle || handle.done || current.name !== "pending" && current.name !== "live" || current.session.followUp === null) {
|
|
1879
|
+
return;
|
|
1880
|
+
}
|
|
1881
|
+
const session = {
|
|
1882
|
+
...current.session,
|
|
1883
|
+
...spoken,
|
|
1884
|
+
followUp: { ...current.session.followUp, ...next }
|
|
1885
|
+
};
|
|
1886
|
+
go(
|
|
1887
|
+
current.name === "live" ? { name: "live", session } : { name: "pending", session }
|
|
1888
|
+
);
|
|
1889
|
+
},
|
|
1890
|
+
[go]
|
|
1891
|
+
);
|
|
1892
|
+
const tell = react.useCallback(
|
|
1893
|
+
(handle, kind2) => {
|
|
1894
|
+
if (handle.done || handle.followUps === null) return;
|
|
1895
|
+
void requestTurn({
|
|
1896
|
+
base: handle.base,
|
|
1897
|
+
headers: handle.headers,
|
|
1898
|
+
sessionId: handle.session.sessionId,
|
|
1899
|
+
secret: handle.session.endSecret,
|
|
1900
|
+
kind: kind2
|
|
1901
|
+
});
|
|
1902
|
+
},
|
|
1903
|
+
[]
|
|
1904
|
+
);
|
|
1905
|
+
const whenQuiet = react.useCallback(
|
|
1906
|
+
(handle, delay) => {
|
|
1907
|
+
const run = handle.followUps;
|
|
1908
|
+
if (run === null) return;
|
|
1909
|
+
const wait = (ms) => {
|
|
1910
|
+
if (run.quiet !== null) clearTimeout(run.quiet);
|
|
1911
|
+
run.quiet = setTimeout(() => {
|
|
1912
|
+
run.quiet = null;
|
|
1913
|
+
if (sessionRef.current !== handle || handle.done || run.voice) {
|
|
1914
|
+
return;
|
|
1915
|
+
}
|
|
1916
|
+
if (run.owed) {
|
|
1917
|
+
run.owed = false;
|
|
1918
|
+
wait(OWED_VOICE_WAIT_MS);
|
|
1919
|
+
return;
|
|
1920
|
+
}
|
|
1921
|
+
handle.playedOut = true;
|
|
1922
|
+
showFollowUp(handle, { speaking: false });
|
|
1923
|
+
attend();
|
|
1924
|
+
sendDueRef.current(handle);
|
|
1925
|
+
}, ms);
|
|
1926
|
+
};
|
|
1927
|
+
wait(delay);
|
|
1928
|
+
},
|
|
1929
|
+
[attend, showFollowUp]
|
|
1930
|
+
);
|
|
1931
|
+
const heardVoice = react.useCallback(
|
|
1932
|
+
(handle, speaking) => {
|
|
1933
|
+
const run = handle.followUps;
|
|
1934
|
+
if (run === null) return;
|
|
1935
|
+
run.voice = speaking;
|
|
1936
|
+
if (!speaking) {
|
|
1937
|
+
whenQuiet(handle, QUIET_PLAYED_OUT_MS);
|
|
1938
|
+
return;
|
|
1939
|
+
}
|
|
1940
|
+
if (run.quiet !== null) clearTimeout(run.quiet);
|
|
1941
|
+
run.quiet = null;
|
|
1942
|
+
run.heard = true;
|
|
1943
|
+
run.starts += 1;
|
|
1944
|
+
run.owed = false;
|
|
1945
|
+
handle.playedOut = false;
|
|
1946
|
+
showFollowUp(handle, { speaking: true });
|
|
1947
|
+
},
|
|
1948
|
+
[showFollowUp, whenQuiet]
|
|
1949
|
+
);
|
|
1950
|
+
const sendDue = react.useCallback(
|
|
1951
|
+
(handle) => {
|
|
1952
|
+
const run = handle.followUps;
|
|
1953
|
+
const due = run?.due ?? null;
|
|
1954
|
+
if (run === null || due === null || due.sending || run.retry !== null || handle.done || sessionRef.current !== handle || run.heard && !handle.playedOut) {
|
|
1955
|
+
return;
|
|
1956
|
+
}
|
|
1957
|
+
due.sending = true;
|
|
1958
|
+
const starts = run.starts;
|
|
1959
|
+
const read = () => requestTurn({
|
|
1960
|
+
base: handle.base,
|
|
1961
|
+
headers: handle.headers,
|
|
1962
|
+
sessionId: handle.session.sessionId,
|
|
1963
|
+
secret: handle.session.endSecret,
|
|
1964
|
+
kind: "answer",
|
|
1965
|
+
messageId: due.messageId,
|
|
1966
|
+
idempotencyKey: due.idempotencyKey
|
|
1967
|
+
});
|
|
1968
|
+
const notRead = (unread, closed) => {
|
|
1969
|
+
run.due = null;
|
|
1970
|
+
showFollowUp(handle, {
|
|
1971
|
+
pending: null,
|
|
1972
|
+
unread: due.known ?? unread,
|
|
1973
|
+
...closed ? { closed } : {}
|
|
1974
|
+
});
|
|
1975
|
+
attend();
|
|
1976
|
+
};
|
|
1977
|
+
void (async () => {
|
|
1978
|
+
let result = await read();
|
|
1979
|
+
if (result.kind === "failed" && result.lost && !handle.done) {
|
|
1980
|
+
result = await read();
|
|
1981
|
+
}
|
|
1982
|
+
due.sending = false;
|
|
1983
|
+
if (sessionRef.current !== handle || handle.done || run.due !== due) {
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1986
|
+
if (result.kind === "refused" && result.reason === "busy" && due.busy < MAX_BUSY_RETRIES) {
|
|
1987
|
+
due.busy += 1;
|
|
1988
|
+
if (run.heard && !handle.playedOut) return;
|
|
1989
|
+
const delay = Math.max(
|
|
1990
|
+
result.retryAfterSeconds === void 0 ? BUSY_RETRY_MS : result.retryAfterSeconds * 1e3,
|
|
1991
|
+
Math.min(BUSY_RETRY_MAX_MS, BUSY_RETRY_MS * 2 ** (due.busy - 1))
|
|
1992
|
+
);
|
|
1993
|
+
if (Date.now() + delay >= handle.endsAt) {
|
|
1994
|
+
notRead("no_time", false);
|
|
1995
|
+
return;
|
|
1996
|
+
}
|
|
1997
|
+
run.retry = setTimeout(() => {
|
|
1998
|
+
run.retry = null;
|
|
1999
|
+
sendDueRef.current(handle);
|
|
2000
|
+
}, delay);
|
|
2001
|
+
return;
|
|
2002
|
+
}
|
|
2003
|
+
if (result.kind !== "accepted") {
|
|
2004
|
+
notRead(
|
|
2005
|
+
result.kind === "refused" ? result.reason : "failed",
|
|
2006
|
+
result.kind === "gone" || result.kind === "refused" && result.reason === "max_answers"
|
|
2007
|
+
);
|
|
2008
|
+
return;
|
|
2009
|
+
}
|
|
2010
|
+
run.due = null;
|
|
2011
|
+
run.answers += 1;
|
|
2012
|
+
const heardSince = run.starts !== starts;
|
|
2013
|
+
run.owed = !heardSince;
|
|
2014
|
+
const playing = !heardSince || run.voice || run.quiet !== null;
|
|
2015
|
+
if (playing) handle.playedOut = false;
|
|
2016
|
+
showFollowUp(
|
|
2017
|
+
handle,
|
|
2018
|
+
{
|
|
2019
|
+
speaking: playing,
|
|
2020
|
+
pending: null,
|
|
2021
|
+
unread: null,
|
|
2022
|
+
closed: run.answers >= run.limits.maxAnswers
|
|
2023
|
+
},
|
|
2024
|
+
{ captions: result.captions, excerpt: result.excerpt }
|
|
2025
|
+
);
|
|
2026
|
+
if (!playing) {
|
|
2027
|
+
attend();
|
|
2028
|
+
return;
|
|
2029
|
+
}
|
|
2030
|
+
if (run.owed && run.heard && !run.voice) whenQuiet(handle, 0);
|
|
2031
|
+
})();
|
|
2032
|
+
},
|
|
2033
|
+
[attend, showFollowUp, whenQuiet]
|
|
2034
|
+
);
|
|
2035
|
+
sendDueRef.current = sendDue;
|
|
2036
|
+
const readFollowUp = react.useCallback(
|
|
2037
|
+
(handle, messageId, known) => {
|
|
2038
|
+
const run = handle.followUps;
|
|
2039
|
+
if (run === null) return;
|
|
2040
|
+
run.sent.add(messageId);
|
|
2041
|
+
const idempotencyKey = newIdempotencyKey();
|
|
2042
|
+
if (idempotencyKey === null) {
|
|
2043
|
+
showFollowUp(handle, { pending: null, unread: known ?? "failed" });
|
|
2044
|
+
attend();
|
|
2045
|
+
return;
|
|
2046
|
+
}
|
|
2047
|
+
run.due = { messageId, idempotencyKey, known, busy: 0, sending: false };
|
|
2048
|
+
showFollowUp(handle, { pending: "reading", unread: null });
|
|
2049
|
+
sendDue(handle);
|
|
2050
|
+
},
|
|
2051
|
+
[attend, sendDue, showFollowUp]
|
|
2052
|
+
);
|
|
1285
2053
|
const cancelStart = react.useCallback((reason) => {
|
|
1286
2054
|
const run = startRef.current;
|
|
1287
2055
|
if (!run) return;
|
|
@@ -1292,6 +2060,7 @@ function AvatarLiveView({
|
|
|
1292
2060
|
const applyAvailability = react.useCallback(
|
|
1293
2061
|
(result, locale2) => {
|
|
1294
2062
|
const { live: live2 } = latest.current;
|
|
2063
|
+
setMicOffer(micOfferOf(result));
|
|
1295
2064
|
if (result.kind === "inert") {
|
|
1296
2065
|
setMode("suggested");
|
|
1297
2066
|
go(INERT2);
|
|
@@ -1309,7 +2078,7 @@ function AvatarLiveView({
|
|
|
1309
2078
|
go(INERT2);
|
|
1310
2079
|
return;
|
|
1311
2080
|
}
|
|
1312
|
-
if (result.offer.botCheck === "turnstile" &&
|
|
2081
|
+
if (result.offer.botCheck === "turnstile" && chunkXIZE37IB_js.turnstileSiteKeyOf(live2) === null) {
|
|
1313
2082
|
setMode("suggested");
|
|
1314
2083
|
go(INERT2);
|
|
1315
2084
|
return;
|
|
@@ -1344,16 +2113,32 @@ function AvatarLiveView({
|
|
|
1344
2113
|
});
|
|
1345
2114
|
}, [applyAvailability, go]);
|
|
1346
2115
|
const begin = react.useCallback(
|
|
1347
|
-
(session,
|
|
2116
|
+
(session, context) => {
|
|
2117
|
+
const { offer } = context;
|
|
1348
2118
|
const received = Date.now();
|
|
1349
2119
|
const longest = offer.maxSessionSeconds * 1e3;
|
|
1350
2120
|
const ahead = session.endsAt - received;
|
|
1351
2121
|
const endsAt = ahead > 0 && ahead <= longest + CLOCK_TOLERANCE_MS ? session.endsAt : received + longest;
|
|
2122
|
+
const followUps = session.followUps !== void 0 && offer.copy.followUps === true && offer.questions === "any" ? {
|
|
2123
|
+
limits: session.followUps,
|
|
2124
|
+
token: context.conversationToken,
|
|
2125
|
+
answers: 1,
|
|
2126
|
+
voice: false,
|
|
2127
|
+
heard: false,
|
|
2128
|
+
starts: 0,
|
|
2129
|
+
owed: true,
|
|
2130
|
+
quiet: null,
|
|
2131
|
+
asked: null,
|
|
2132
|
+
due: null,
|
|
2133
|
+
retry: null,
|
|
2134
|
+
sent: /* @__PURE__ */ new Set([context.messageId]),
|
|
2135
|
+
seq: 0
|
|
2136
|
+
} : null;
|
|
1352
2137
|
sessionRef.current = {
|
|
1353
2138
|
session,
|
|
1354
|
-
base:
|
|
1355
|
-
headers:
|
|
1356
|
-
module,
|
|
2139
|
+
base: context.base,
|
|
2140
|
+
headers: context.headers,
|
|
2141
|
+
module: context.module,
|
|
1357
2142
|
room: null,
|
|
1358
2143
|
joined: null,
|
|
1359
2144
|
endsAt,
|
|
@@ -1363,6 +2148,7 @@ function AvatarLiveView({
|
|
|
1363
2148
|
playedOut: false,
|
|
1364
2149
|
unheard: null,
|
|
1365
2150
|
tail: null,
|
|
2151
|
+
followUps,
|
|
1366
2152
|
done: false
|
|
1367
2153
|
};
|
|
1368
2154
|
setBlocked(false);
|
|
@@ -1372,7 +2158,13 @@ function AvatarLiveView({
|
|
|
1372
2158
|
session: {
|
|
1373
2159
|
endsAt,
|
|
1374
2160
|
captions: session.captions,
|
|
1375
|
-
excerpt: session.excerpt
|
|
2161
|
+
excerpt: session.excerpt,
|
|
2162
|
+
followUp: followUps === null ? null : {
|
|
2163
|
+
speaking: true,
|
|
2164
|
+
pending: null,
|
|
2165
|
+
unread: null,
|
|
2166
|
+
closed: followUps.answers >= followUps.limits.maxAnswers
|
|
2167
|
+
}
|
|
1376
2168
|
}
|
|
1377
2169
|
});
|
|
1378
2170
|
},
|
|
@@ -1404,7 +2196,7 @@ function AvatarLiveView({
|
|
|
1404
2196
|
}
|
|
1405
2197
|
switch (result.kind) {
|
|
1406
2198
|
case "created":
|
|
1407
|
-
begin(result.session, context
|
|
2199
|
+
begin(result.session, context);
|
|
1408
2200
|
return;
|
|
1409
2201
|
case "unusable":
|
|
1410
2202
|
sendEnd({
|
|
@@ -1446,7 +2238,7 @@ function AvatarLiveView({
|
|
|
1446
2238
|
return;
|
|
1447
2239
|
}
|
|
1448
2240
|
const refusedFrom = exchangeAtStart.offerUntil - ANSWER_OFFER_MS + SERVICE_MAX_AGE_MS;
|
|
1449
|
-
const siteKey = offer.botCheck === "turnstile" ?
|
|
2241
|
+
const siteKey = offer.botCheck === "turnstile" ? chunkXIZE37IB_js.turnstileSiteKeyOf(live2) : null;
|
|
1450
2242
|
if (offer.botCheck === "turnstile" && (siteKey === null || !canBindBotCheck())) {
|
|
1451
2243
|
go(INERT2);
|
|
1452
2244
|
return;
|
|
@@ -1502,7 +2294,14 @@ function AvatarLiveView({
|
|
|
1502
2294
|
return;
|
|
1503
2295
|
}
|
|
1504
2296
|
if (turnstile !== null) go({ name: "creating" });
|
|
1505
|
-
const context = {
|
|
2297
|
+
const context = {
|
|
2298
|
+
module: loaded,
|
|
2299
|
+
offer,
|
|
2300
|
+
messageId,
|
|
2301
|
+
conversationToken,
|
|
2302
|
+
base: base2,
|
|
2303
|
+
headers: headers2
|
|
2304
|
+
};
|
|
1506
2305
|
const result = await requestCreate({
|
|
1507
2306
|
base: base2,
|
|
1508
2307
|
headers: headers2,
|
|
@@ -1607,6 +2406,7 @@ function AvatarLiveView({
|
|
|
1607
2406
|
[applyAvailability, bindingHolds, cancelWaiting, exchangeNow, launch]
|
|
1608
2407
|
);
|
|
1609
2408
|
const stop = react.useCallback(() => {
|
|
2409
|
+
micCancelRef.current();
|
|
1610
2410
|
const current = phaseRef.current;
|
|
1611
2411
|
if (current.name === "waiting") {
|
|
1612
2412
|
cancelWaiting(false);
|
|
@@ -1628,6 +2428,7 @@ function AvatarLiveView({
|
|
|
1628
2428
|
}, [cancelStart, cancelWaiting, finish, go, reoffer]);
|
|
1629
2429
|
const leave = react.useCallback(
|
|
1630
2430
|
(event) => {
|
|
2431
|
+
micCancelRef.current();
|
|
1631
2432
|
const current = phaseRef.current;
|
|
1632
2433
|
const reason = event === "user" || event === "chat" ? "user" : "page_hide";
|
|
1633
2434
|
const how = reason === "user" ? "stopped" : "interrupted";
|
|
@@ -1638,7 +2439,7 @@ function AvatarLiveView({
|
|
|
1638
2439
|
const controller = checkRef.current;
|
|
1639
2440
|
checkRef.current = null;
|
|
1640
2441
|
controller?.abort();
|
|
1641
|
-
go(
|
|
2442
|
+
go(IDLE2);
|
|
1642
2443
|
return;
|
|
1643
2444
|
}
|
|
1644
2445
|
case "waiting":
|
|
@@ -1646,7 +2447,7 @@ function AvatarLiveView({
|
|
|
1646
2447
|
return;
|
|
1647
2448
|
case "bot-check":
|
|
1648
2449
|
cancelStart(reason);
|
|
1649
|
-
go(reason === "user" ?
|
|
2450
|
+
go(reason === "user" ? IDLE2 : reoffer(current.offer));
|
|
1650
2451
|
return;
|
|
1651
2452
|
case "creating":
|
|
1652
2453
|
if (keepsSession) return;
|
|
@@ -1708,7 +2509,7 @@ function AvatarLiveView({
|
|
|
1708
2509
|
} else {
|
|
1709
2510
|
checkRef.current?.abort();
|
|
1710
2511
|
checkRef.current = null;
|
|
1711
|
-
go(
|
|
2512
|
+
go(IDLE2);
|
|
1712
2513
|
}
|
|
1713
2514
|
}, [active, cancelWaiting, check, go, locale]);
|
|
1714
2515
|
const kind = shown.kind;
|
|
@@ -1805,6 +2606,9 @@ function AvatarLiveView({
|
|
|
1805
2606
|
attend();
|
|
1806
2607
|
}, PLAYOUT_TAIL_MS);
|
|
1807
2608
|
return;
|
|
2609
|
+
case "speaking":
|
|
2610
|
+
heardVoice(handle, event.speaking);
|
|
2611
|
+
return;
|
|
1808
2612
|
case "speaker-left":
|
|
1809
2613
|
finish(
|
|
1810
2614
|
wasLive ? "finished" : "failed",
|
|
@@ -1817,7 +2621,7 @@ function AvatarLiveView({
|
|
|
1817
2621
|
return;
|
|
1818
2622
|
}
|
|
1819
2623
|
},
|
|
1820
|
-
[attend, finish, goLive, inSight]
|
|
2624
|
+
[attend, finish, goLive, heardVoice, inSight]
|
|
1821
2625
|
);
|
|
1822
2626
|
react.useEffect(() => {
|
|
1823
2627
|
if (phase.name !== "pending") return;
|
|
@@ -1953,6 +2757,85 @@ function AvatarLiveView({
|
|
|
1953
2757
|
const textMode = any && (usedToday && (phase.name === "needs-answer" || phase.name === "consent" || phase.name === "declined" || phase.name === "ended") || phase.name === "refused" && phase.reason === "limits");
|
|
1954
2758
|
textBoxRef.current = textMode;
|
|
1955
2759
|
const askState = !any ? void 0 : textMode ? "text" : phase.name === "needs-answer" ? kind === "empty" ? "ready" : kind : phase.name;
|
|
2760
|
+
const followUp = phase.name === "live" ? phase.session.followUp : null;
|
|
2761
|
+
const followUpAsks = followUpBox(
|
|
2762
|
+
phase,
|
|
2763
|
+
sessionRef.current?.followUps ?? null,
|
|
2764
|
+
token
|
|
2765
|
+
);
|
|
2766
|
+
const askReason = inFlight(conversation) || followUp !== null && followUp.pending !== null ? S.askBusy : conversation?.canSend ? null : S.askUnavailable;
|
|
2767
|
+
const refreshMicOffer = react.useCallback(async () => {
|
|
2768
|
+
const { live: live2, locale: locale2, base: base2, headers: headers2 } = latest.current;
|
|
2769
|
+
if (base2 === null) return null;
|
|
2770
|
+
const next = micOfferOf(
|
|
2771
|
+
await requestAvailabilityAnswer({
|
|
2772
|
+
base: base2,
|
|
2773
|
+
headers: headers2,
|
|
2774
|
+
site: live2.site,
|
|
2775
|
+
locale: locale2
|
|
2776
|
+
})
|
|
2777
|
+
);
|
|
2778
|
+
if (next !== null) setMicOffer(next);
|
|
2779
|
+
return next;
|
|
2780
|
+
}, []);
|
|
2781
|
+
const onMicText = react.useCallback(
|
|
2782
|
+
(text) => {
|
|
2783
|
+
setDraft((current) => withHeardText(current, text, askMaxLength));
|
|
2784
|
+
if (touchFirst) return;
|
|
2785
|
+
rootRef.current?.querySelector(
|
|
2786
|
+
'[data-boff-explore="avatar-live-ask-input"]'
|
|
2787
|
+
)?.focus();
|
|
2788
|
+
},
|
|
2789
|
+
[askMaxLength, touchFirst]
|
|
2790
|
+
);
|
|
2791
|
+
const onMicStart = react.useCallback(() => {
|
|
2792
|
+
const handle = sessionRef.current;
|
|
2793
|
+
const current = phaseRef.current;
|
|
2794
|
+
if (handle === null || handle.followUps === null || handle.done || current.name !== "live" || current.session.followUp?.speaking !== true) {
|
|
2795
|
+
return;
|
|
2796
|
+
}
|
|
2797
|
+
const run = handle.followUps;
|
|
2798
|
+
tell(handle, "interrupt");
|
|
2799
|
+
if (run.quiet !== null) clearTimeout(run.quiet);
|
|
2800
|
+
run.quiet = null;
|
|
2801
|
+
run.owed = false;
|
|
2802
|
+
handle.playedOut = true;
|
|
2803
|
+
showFollowUp(handle, { speaking: false });
|
|
2804
|
+
}, [showFollowUp, tell]);
|
|
2805
|
+
const mic = useAvatarMic({
|
|
2806
|
+
enabled: live.mic === true && micOffer !== null && !off && acceptsQuestion({
|
|
2807
|
+
any,
|
|
2808
|
+
phase: phase.name,
|
|
2809
|
+
kind,
|
|
2810
|
+
textMode,
|
|
2811
|
+
reason: askReason,
|
|
2812
|
+
followUp: followUpAsks
|
|
2813
|
+
}),
|
|
2814
|
+
locale,
|
|
2815
|
+
base,
|
|
2816
|
+
offer: micOffer,
|
|
2817
|
+
refreshOffer: refreshMicOffer,
|
|
2818
|
+
onText: onMicText,
|
|
2819
|
+
onStart: onMicStart
|
|
2820
|
+
});
|
|
2821
|
+
micCancelRef.current = mic.cancel;
|
|
2822
|
+
const micRecording = mic.recording;
|
|
2823
|
+
react.useEffect(() => {
|
|
2824
|
+
const handle = sessionRef.current;
|
|
2825
|
+
if (!micRecording || handle === null || handle.followUps === null || handle.done) {
|
|
2826
|
+
return;
|
|
2827
|
+
}
|
|
2828
|
+
tell(handle, "listening");
|
|
2829
|
+
const every = Math.min(
|
|
2830
|
+
LISTENING_MARK_MS,
|
|
2831
|
+
Math.max(1e3, handle.followUps.limits.idleSeconds * 1e3 / 3)
|
|
2832
|
+
);
|
|
2833
|
+
const timer = setInterval(() => tell(handle, "listening"), every);
|
|
2834
|
+
return () => {
|
|
2835
|
+
clearInterval(timer);
|
|
2836
|
+
if (sessionRef.current === handle) tell(handle, "listening");
|
|
2837
|
+
};
|
|
2838
|
+
}, [micRecording, tell]);
|
|
1956
2839
|
const notNow = react.useCallback(() => {
|
|
1957
2840
|
const current = phaseRef.current;
|
|
1958
2841
|
if ((current.name === "consent" || current.name === "needs-answer") && current.offer.questions === "any") {
|
|
@@ -1961,6 +2844,43 @@ function AvatarLiveView({
|
|
|
1961
2844
|
}
|
|
1962
2845
|
onContinueInChat();
|
|
1963
2846
|
}, [go, onContinueInChat]);
|
|
2847
|
+
const askFollowUp = react.useCallback(
|
|
2848
|
+
(handle, conversation2, question) => {
|
|
2849
|
+
const run = handle.followUps;
|
|
2850
|
+
const current = phaseRef.current;
|
|
2851
|
+
if (run === null || handle.done || current.name !== "live" || current.session.followUp?.pending !== null || !followUpBox(current, run, conversation2.getConversationToken())) {
|
|
2852
|
+
return;
|
|
2853
|
+
}
|
|
2854
|
+
sendingRef.current = true;
|
|
2855
|
+
run.seq += 1;
|
|
2856
|
+
const seq = run.seq;
|
|
2857
|
+
setDraft("");
|
|
2858
|
+
tell(handle, "asking");
|
|
2859
|
+
showFollowUp(handle, { pending: "asking", unread: null });
|
|
2860
|
+
void conversation2.send(question, [], { surface: "AVATAR" }).then((added) => {
|
|
2861
|
+
if (added === null) {
|
|
2862
|
+
setDraft((current2) => current2 === "" ? question : current2);
|
|
2863
|
+
}
|
|
2864
|
+
if (sessionRef.current !== handle || handle.done || run.seq !== seq) {
|
|
2865
|
+
return;
|
|
2866
|
+
}
|
|
2867
|
+
if (added === null || added.conversationToken !== run.token) {
|
|
2868
|
+
showFollowUp(handle, { pending: null });
|
|
2869
|
+
attend();
|
|
2870
|
+
return;
|
|
2871
|
+
}
|
|
2872
|
+
run.asked = {
|
|
2873
|
+
messageId: added.userMessageId,
|
|
2874
|
+
token: added.conversationToken,
|
|
2875
|
+
answerId: null
|
|
2876
|
+
};
|
|
2877
|
+
showFollowUp(handle, { pending: "asking" });
|
|
2878
|
+
}).finally(() => {
|
|
2879
|
+
sendingRef.current = false;
|
|
2880
|
+
});
|
|
2881
|
+
},
|
|
2882
|
+
[attend, showFollowUp, tell]
|
|
2883
|
+
);
|
|
1964
2884
|
const ask = react.useCallback(
|
|
1965
2885
|
(text) => {
|
|
1966
2886
|
const conversation2 = conversationRef.current;
|
|
@@ -1969,6 +2889,11 @@ function AvatarLiveView({
|
|
|
1969
2889
|
return;
|
|
1970
2890
|
}
|
|
1971
2891
|
const spoken = !textBoxRef.current;
|
|
2892
|
+
const handle = sessionRef.current;
|
|
2893
|
+
if (spoken && handle !== null && handle.followUps !== null) {
|
|
2894
|
+
askFollowUp(handle, conversation2, question);
|
|
2895
|
+
return;
|
|
2896
|
+
}
|
|
1972
2897
|
if (spoken && conversation2.messages.length > 0) return;
|
|
1973
2898
|
sendingRef.current = true;
|
|
1974
2899
|
setAskedHere(null);
|
|
@@ -1990,8 +2915,64 @@ function AvatarLiveView({
|
|
|
1990
2915
|
sendingRef.current = false;
|
|
1991
2916
|
});
|
|
1992
2917
|
},
|
|
1993
|
-
[setAskedHere]
|
|
2918
|
+
[askFollowUp, setAskedHere]
|
|
1994
2919
|
);
|
|
2920
|
+
react.useEffect(() => {
|
|
2921
|
+
const handle = sessionRef.current;
|
|
2922
|
+
const run = handle?.followUps ?? null;
|
|
2923
|
+
if (handle === null || run === null || handle.done || run.asked === null) {
|
|
2924
|
+
return;
|
|
2925
|
+
}
|
|
2926
|
+
const asked = run.asked;
|
|
2927
|
+
const chat = conversationRef.current;
|
|
2928
|
+
const messages = chat?.messages ?? [];
|
|
2929
|
+
const at = messages.findIndex((message) => message.id === asked.messageId);
|
|
2930
|
+
const question = at === -1 ? void 0 : messages[at];
|
|
2931
|
+
const answer = at === -1 ? void 0 : messages[at + 1];
|
|
2932
|
+
const drop = (unread2) => {
|
|
2933
|
+
run.asked = null;
|
|
2934
|
+
showFollowUp(handle, { pending: null, unread: unread2 });
|
|
2935
|
+
attend();
|
|
2936
|
+
};
|
|
2937
|
+
const unread = (id, reason) => {
|
|
2938
|
+
if (id === null || run.sent.has(id) || !isAvatarMessageId(id)) {
|
|
2939
|
+
drop(reason);
|
|
2940
|
+
return;
|
|
2941
|
+
}
|
|
2942
|
+
run.asked = null;
|
|
2943
|
+
readFollowUp(handle, id, reason);
|
|
2944
|
+
};
|
|
2945
|
+
if (question === void 0 || (chat?.getConversationToken() ?? null) !== run.token) {
|
|
2946
|
+
drop(null);
|
|
2947
|
+
return;
|
|
2948
|
+
}
|
|
2949
|
+
if (answer === void 0) {
|
|
2950
|
+
if (!inFlight(chat)) unread(asked.answerId, "answer_failed");
|
|
2951
|
+
return;
|
|
2952
|
+
}
|
|
2953
|
+
if (answer.status === "PENDING" || answer.status === "RUNNING") {
|
|
2954
|
+
if (answer.role === "ASSISTANT") asked.answerId = answer.id;
|
|
2955
|
+
return;
|
|
2956
|
+
}
|
|
2957
|
+
if (answer.role !== "ASSISTANT") {
|
|
2958
|
+
unread(asked.answerId, "answer_failed");
|
|
2959
|
+
return;
|
|
2960
|
+
}
|
|
2961
|
+
if (answer.status !== "COMPLETED" || answer.content.trim() === "") {
|
|
2962
|
+
unread(answer.id, "answer_failed");
|
|
2963
|
+
return;
|
|
2964
|
+
}
|
|
2965
|
+
if (run.sent.has(answer.id) || !isAvatarMessageId(answer.id)) {
|
|
2966
|
+
drop("not_eligible");
|
|
2967
|
+
return;
|
|
2968
|
+
}
|
|
2969
|
+
if (at + 2 !== messages.length || !readable(question, answer)) {
|
|
2970
|
+
unread(answer.id, "not_eligible");
|
|
2971
|
+
return;
|
|
2972
|
+
}
|
|
2973
|
+
run.asked = null;
|
|
2974
|
+
readFollowUp(handle, answer.id, null);
|
|
2975
|
+
});
|
|
1995
2976
|
const newChatForAvatar = react.useCallback(() => {
|
|
1996
2977
|
const conversation2 = conversationRef.current;
|
|
1997
2978
|
if (conversation2 === null) return;
|
|
@@ -2015,7 +2996,14 @@ function AvatarLiveView({
|
|
|
2015
2996
|
const focused = document.activeElement;
|
|
2016
2997
|
if (focused && focused !== document.body && focused.isConnected) return;
|
|
2017
2998
|
rootRef.current?.querySelector(PRIMARY)?.focus();
|
|
2018
|
-
}, [
|
|
2999
|
+
}, [
|
|
3000
|
+
active,
|
|
3001
|
+
phase.name,
|
|
3002
|
+
askState,
|
|
3003
|
+
shown.pendingAnswerId,
|
|
3004
|
+
blocked,
|
|
3005
|
+
followUpAsks
|
|
3006
|
+
]);
|
|
2019
3007
|
const onPlaying = goLive;
|
|
2020
3008
|
const enablePlayback = react.useCallback(() => {
|
|
2021
3009
|
void sessionRef.current?.joined?.enablePlayback().catch(() => void 0);
|
|
@@ -2121,7 +3109,7 @@ function AvatarLiveView({
|
|
|
2121
3109
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2122
3110
|
"a",
|
|
2123
3111
|
{
|
|
2124
|
-
href:
|
|
3112
|
+
href: PRIVACY_HREF2,
|
|
2125
3113
|
target: "_blank",
|
|
2126
3114
|
rel: "noopener noreferrer",
|
|
2127
3115
|
"data-boff-explore": "avatar-live-privacy",
|
|
@@ -2213,7 +3201,7 @@ function AvatarLiveView({
|
|
|
2213
3201
|
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm tabular-nums text-muted-foreground", children: [
|
|
2214
3202
|
S.timeLeft,
|
|
2215
3203
|
" ",
|
|
2216
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { role: "timer", "data-boff-explore": "avatar-live-countdown", children:
|
|
3204
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { role: "timer", "data-boff-explore": "avatar-live-countdown", children: clock2(Math.max(0, (session.endsAt - now) / 1e3)) })
|
|
2217
3205
|
] })
|
|
2218
3206
|
] });
|
|
2219
3207
|
const liveActions = (back) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2", children: [
|
|
@@ -2415,13 +3403,32 @@ function AvatarLiveView({
|
|
|
2415
3403
|
actions2 = stopButton;
|
|
2416
3404
|
break;
|
|
2417
3405
|
case "pending":
|
|
2418
|
-
case "live":
|
|
2419
|
-
status = phase.name === "live" ? speakingLine(phase.session) : S.connecting;
|
|
3406
|
+
case "live": {
|
|
2420
3407
|
top2 = badge;
|
|
2421
3408
|
media2 = liveMedia(phase.name === "live");
|
|
2422
3409
|
body2 = liveBody(phase.session);
|
|
2423
3410
|
actions2 = liveActions(readInChatButton(false));
|
|
3411
|
+
if (followUp === null) {
|
|
3412
|
+
status = phase.name === "live" ? speakingLine(phase.session) : S.connecting;
|
|
3413
|
+
break;
|
|
3414
|
+
}
|
|
3415
|
+
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;
|
|
3416
|
+
if (!followUp.speaking && followUp.pending === null && followUp.unread !== null && !followUp.closed) {
|
|
3417
|
+
body2 = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3418
|
+
body2,
|
|
3419
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3420
|
+
"p",
|
|
3421
|
+
{
|
|
3422
|
+
"data-boff-explore": "avatar-live-idle",
|
|
3423
|
+
className: "text-xs text-muted-foreground",
|
|
3424
|
+
children: S.followUpIdle
|
|
3425
|
+
}
|
|
3426
|
+
)
|
|
3427
|
+
] });
|
|
3428
|
+
}
|
|
3429
|
+
if (followUpAsks) box = "spoken";
|
|
2424
3430
|
break;
|
|
3431
|
+
}
|
|
2425
3432
|
case "ended":
|
|
2426
3433
|
status = S[phase.how];
|
|
2427
3434
|
outcome2 = true;
|
|
@@ -2430,7 +3437,7 @@ function AvatarLiveView({
|
|
|
2430
3437
|
break;
|
|
2431
3438
|
}
|
|
2432
3439
|
}
|
|
2433
|
-
const reason =
|
|
3440
|
+
const reason = askReason;
|
|
2434
3441
|
const chips = box !== null && shown.kind === "empty" && prompts.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
2435
3442
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-medium text-muted-foreground", children: tryAsking }),
|
|
2436
3443
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex max-h-28 flex-wrap gap-2 overflow-y-auto", children: prompts.map((prompt) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2469,7 +3476,7 @@ function AvatarLiveView({
|
|
|
2469
3476
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: S.newChatNote })
|
|
2470
3477
|
] }) : null,
|
|
2471
3478
|
chips,
|
|
2472
|
-
box !== null && usedToday ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3479
|
+
box !== null && usedToday && phase.name !== "live" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2473
3480
|
"p",
|
|
2474
3481
|
{
|
|
2475
3482
|
"data-boff-explore": "avatar-live-used-today",
|
|
@@ -2477,6 +3484,7 @@ function AvatarLiveView({
|
|
|
2477
3484
|
children: S.usedToday
|
|
2478
3485
|
}
|
|
2479
3486
|
) : null,
|
|
3487
|
+
box === "spoken" ? mic.notice : null,
|
|
2480
3488
|
box === null ? null : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2481
3489
|
AskBox,
|
|
2482
3490
|
{
|
|
@@ -2491,7 +3499,9 @@ function AvatarLiveView({
|
|
|
2491
3499
|
footer: S.askFooter,
|
|
2492
3500
|
noFiles: S.askNoFiles,
|
|
2493
3501
|
hint: box === "spoken" ? S.needsAnswerAny : null,
|
|
2494
|
-
touchFirst
|
|
3502
|
+
touchFirst,
|
|
3503
|
+
mic: box === "spoken" ? mic.button : null,
|
|
3504
|
+
micLines: box === "spoken" ? mic.lines : null
|
|
2495
3505
|
}
|
|
2496
3506
|
)
|
|
2497
3507
|
]
|
|
@@ -2638,7 +3648,9 @@ function AskBox({
|
|
|
2638
3648
|
footer,
|
|
2639
3649
|
noFiles,
|
|
2640
3650
|
hint,
|
|
2641
|
-
touchFirst
|
|
3651
|
+
touchFirst,
|
|
3652
|
+
mic,
|
|
3653
|
+
micLines
|
|
2642
3654
|
}) {
|
|
2643
3655
|
const uid = react.useId();
|
|
2644
3656
|
const reasonId = `${uid}-reason`;
|
|
@@ -2703,6 +3715,7 @@ function AskBox({
|
|
|
2703
3715
|
onDrop
|
|
2704
3716
|
}
|
|
2705
3717
|
),
|
|
3718
|
+
mic,
|
|
2706
3719
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2707
3720
|
"button",
|
|
2708
3721
|
{
|
|
@@ -2716,6 +3729,7 @@ function AskBox({
|
|
|
2716
3729
|
}
|
|
2717
3730
|
)
|
|
2718
3731
|
] }),
|
|
3732
|
+
micLines,
|
|
2719
3733
|
reason === null ? null : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2720
3734
|
"p",
|
|
2721
3735
|
{
|