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