@burdenoff/website-sdk 2026.1102.2 → 2026.1104.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +63 -8
  2. package/dist/avatar-live-view/index.d.mts +29 -5
  3. package/dist/avatar-live-view/index.d.ts +29 -5
  4. package/dist/avatar-live-view/index.js +1319 -271
  5. package/dist/avatar-live-view/index.js.map +1 -1
  6. package/dist/avatar-live-view/index.mjs +1317 -269
  7. package/dist/avatar-live-view/index.mjs.map +1 -1
  8. package/dist/{chunk-R742FS5E.js → chunk-22OT6E26.js} +61 -27
  9. package/dist/chunk-22OT6E26.js.map +1 -0
  10. package/dist/{chunk-ETI2HQEC.mjs → chunk-5JQGE3XI.mjs} +46 -14
  11. package/dist/chunk-5JQGE3XI.mjs.map +1 -0
  12. package/dist/{chunk-FQH3AJN3.js → chunk-7O2I32ZN.js} +2 -2
  13. package/dist/chunk-7O2I32ZN.js.map +1 -0
  14. package/dist/{chunk-SFT3WHN5.mjs → chunk-K3QZGJEH.mjs} +2 -2
  15. package/dist/chunk-K3QZGJEH.mjs.map +1 -0
  16. package/dist/{chunk-SK5UGG6A.js → chunk-P5GMVPR6.js} +76 -20
  17. package/dist/chunk-P5GMVPR6.js.map +1 -0
  18. package/dist/{chunk-EUPFS6MZ.mjs → chunk-SMD7MEEA.mjs} +76 -20
  19. package/dist/chunk-SMD7MEEA.mjs.map +1 -0
  20. package/dist/components/explore.d.mts +2 -2
  21. package/dist/components/explore.d.ts +2 -2
  22. package/dist/components/explore.js +14 -14
  23. package/dist/components/explore.mjs +2 -2
  24. package/dist/{explore-DUTNYUOm.d.mts → explore-BV4dRwoC.d.ts} +8 -1
  25. package/dist/{explore-DjQE7Xia.d.ts → explore-BgEeGPvY.d.mts} +8 -1
  26. package/dist/{explore-avatar-live-config-Cetx8-Zu.d.ts → explore-avatar-live-config-CAfN5eAX.d.ts} +6 -3
  27. package/dist/{explore-avatar-live-config-Bp3kQbU2.d.mts → explore-avatar-live-config-N4WhJzgS.d.mts} +6 -3
  28. package/dist/{explore-types-Bb2Ih3d8.d.mts → explore-types-Ol1Mm3dW.d.mts} +12 -1
  29. package/dist/{explore-types-Bb2Ih3d8.d.ts → explore-types-Ol1Mm3dW.d.ts} +12 -1
  30. package/dist/hooks/index.d.mts +2 -2
  31. package/dist/hooks/index.d.ts +2 -2
  32. package/dist/hooks/index.js +9 -9
  33. package/dist/hooks/index.mjs +1 -1
  34. package/dist/index.d.mts +14 -8
  35. package/dist/index.d.ts +14 -8
  36. package/dist/index.js +100 -63
  37. package/dist/index.js.map +1 -1
  38. package/dist/index.mjs +43 -6
  39. package/dist/index.mjs.map +1 -1
  40. package/dist/{use-explore-chat-DunjG-3p.d.ts → use-explore-chat-B3dOOvJN.d.ts} +43 -3
  41. package/dist/{use-explore-chat-zRuoAin6.d.mts → use-explore-chat-CbfohA9O.d.mts} +43 -3
  42. package/package.json +1 -1
  43. package/dist/chunk-ETI2HQEC.mjs.map +0 -1
  44. package/dist/chunk-EUPFS6MZ.mjs.map +0 -1
  45. package/dist/chunk-FQH3AJN3.js.map +0 -1
  46. package/dist/chunk-R742FS5E.js.map +0 -1
  47. package/dist/chunk-SFT3WHN5.mjs.map +0 -1
  48. package/dist/chunk-SK5UGG6A.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { turnstileSiteKeyOf, isAvatarSite, isAvatarLiveLocaleReviewed, isAvatarVersion } from '../chunk-SFT3WHN5.mjs';
1
+ import { turnstileSiteKeyOf, isAvatarLiveLocaleReviewed, isAvatarSite, isAvatarVersion } from '../chunk-K3QZGJEH.mjs';
2
2
  import { fill } from '../chunk-TT3MHN42.mjs';
3
3
  import { cn } from '../chunk-R7G6TWIS.mjs';
4
4
  import '../chunk-VNS5WDWO.mjs';
@@ -47,6 +47,15 @@ function readBotCheck(value) {
47
47
  }
48
48
  return mode === "off" ? "off" : "turnstile";
49
49
  }
50
+ var QUESTION_MODES = /* @__PURE__ */ new Set([
51
+ "any",
52
+ "suggested"
53
+ ]);
54
+ function readQuestions(value) {
55
+ if (value === void 0) return "suggested";
56
+ if (typeof value !== "string" || !QUESTION_MODES.has(value)) return null;
57
+ return value === "any" ? "any" : "suggested";
58
+ }
50
59
  function readAvailability(status, body) {
51
60
  if (status !== 200 || !isRecord(body)) return INERT;
52
61
  const { available, reason } = body;
@@ -56,16 +65,21 @@ function readAvailability(status, body) {
56
65
  if (reason === "ok") {
57
66
  const { consentVersion, maxSessionSeconds } = body;
58
67
  const botCheck = readBotCheck(body.botCheck);
59
- if (available !== true || typeof consentVersion !== "string" || !isAvatarVersion(consentVersion) || typeof maxSessionSeconds !== "number" || !Number.isInteger(maxSessionSeconds) || maxSessionSeconds < 1 || botCheck === null) {
68
+ const questions = readQuestions(body.questions);
69
+ if (available !== true || typeof consentVersion !== "string" || !isAvatarVersion(consentVersion) || typeof maxSessionSeconds !== "number" || !Number.isInteger(maxSessionSeconds) || maxSessionSeconds < 1 || botCheck === null || questions === null) {
60
70
  return INERT;
61
71
  }
62
72
  return {
63
73
  kind: "available",
64
- offer: { consentVersion, maxSessionSeconds, botCheck }
74
+ offer: { consentVersion, maxSessionSeconds, botCheck, questions }
65
75
  };
66
76
  }
67
77
  const meaning = available ? void 0 : AVAILABILITY_REASONS.get(reason);
68
- return meaning === void 0 || meaning === "inert" ? INERT : { kind: "refused", reason: meaning };
78
+ return meaning === void 0 || meaning === "inert" ? INERT : {
79
+ kind: "refused",
80
+ reason: meaning,
81
+ questions: body.questions === "any" ? "any" : "suggested"
82
+ };
69
83
  }
70
84
  var CREATE_REFUSALS = /* @__PURE__ */ new Map([
71
85
  ["busy", "busy"],
@@ -285,7 +299,31 @@ var EN = {
285
299
  locale: "The live avatar isn't offered in this language yet.",
286
300
  paused: "The live avatar is paused right now.",
287
301
  notEligible: "The avatar reads the first answer to a suggested question, within fifteen minutes. Start a new chat and choose a suggested question.",
288
- answerClaimed: "The avatar has already read this answer."
302
+ answerClaimed: "The avatar has already read this answer.",
303
+ askHeadline: "Ask a question, and the avatar reads the answer aloud.",
304
+ askLabel: "Question for the avatar",
305
+ askPlaceholder: "Ask the avatar\u2026",
306
+ askFooter: "Answers are AI-generated and can be wrong. Don't include personal details.",
307
+ askNoFiles: "To attach files, use Chat.",
308
+ askBusy: "You can ask when the current answer is ready.",
309
+ askUnavailable: "Chat isn't available right now.",
310
+ gettingAnswer: "Getting the answer\u2026",
311
+ startWhenReady: "Start when ready",
312
+ armed: "The avatar will start when the answer is ready.",
313
+ cancel: "Cancel",
314
+ armedCancelled: "The avatar didn't start. Press Start when ready to try again.",
315
+ notSpoken: "This answer stays in Chat as text. The avatar reads only answers that pass its speech checks.",
316
+ answerFailed: "The assistant couldn't answer. Try asking again.",
317
+ declined: "No problem. The answer is in Chat.",
318
+ askHere: "To hear an answer, ask it here.",
319
+ newChatForAvatar: "New chat for the avatar",
320
+ newChatNote: "Your current chat stays in History.",
321
+ newChatStarted: "Started a new chat for the avatar.",
322
+ readInChat: "Read the answer and sources in Chat",
323
+ usedToday: "You've had today's avatar session. Answers will show here as text.",
324
+ needsAnswerAny: "Ask a question here, and the avatar reads the answer aloud.",
325
+ startNewChatDetailAny: "The avatar reads aloud only the first answer in a new chat started in the Avatar tab, provided it passes the speech checks.",
326
+ notEligibleAny: "The avatar can't read this answer aloud. The answer and sources are in Chat."
289
327
  };
290
328
  var ZH = {
291
329
  checking: "\u6B63\u5728\u68C0\u67E5\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u662F\u5426\u53EF\u7528\u2026",
@@ -319,7 +357,32 @@ var ZH = {
319
357
  paused: "\u5B9E\u65F6\u865A\u62DF\u5F62\u8C61\u76EE\u524D\u5DF2\u6682\u505C\u3002",
320
358
  // TRANSLATION REVIEW PENDING (BOFF-9797): draft.
321
359
  notEligible: "\u865A\u62DF\u5F62\u8C61\u53EA\u6717\u8BFB\u63A8\u8350\u95EE\u9898\u7684\u7B2C\u4E00\u4E2A\u56DE\u7B54\uFF0C\u4E14\u987B\u5728\u5341\u4E94\u5206\u949F\u5185\u3002\u8BF7\u5F00\u59CB\u65B0\u7684\u804A\u5929\uFF0C\u5E76\u9009\u62E9\u4E00\u4E2A\u63A8\u8350\u95EE\u9898\u3002",
322
- answerClaimed: "\u865A\u62DF\u5F62\u8C61\u5DF2\u7ECF\u6717\u8BFB\u8FC7\u8FD9\u4E2A\u56DE\u7B54\u3002"
360
+ answerClaimed: "\u865A\u62DF\u5F62\u8C61\u5DF2\u7ECF\u6717\u8BFB\u8FC7\u8FD9\u4E2A\u56DE\u7B54\u3002",
361
+ // TRANSLATION REVIEW PENDING (BOFF-9797): draft, the Avatar tab's question box.
362
+ askHeadline: "\u63D0\u51FA\u95EE\u9898\uFF0C\u865A\u62DF\u5F62\u8C61\u4F1A\u6717\u8BFB\u56DE\u7B54\u3002",
363
+ askLabel: "\u5411\u865A\u62DF\u5F62\u8C61\u63D0\u7684\u95EE\u9898",
364
+ askPlaceholder: "\u5411\u865A\u62DF\u5F62\u8C61\u63D0\u95EE\u2026",
365
+ askFooter: "\u56DE\u7B54\u7531 AI \u751F\u6210\uFF0C\u53EF\u80FD\u6709\u8BEF\u3002\u8BF7\u52FF\u5305\u542B\u4E2A\u4EBA\u4FE1\u606F\u3002",
366
+ askNoFiles: "\u5982\u9700\u9644\u52A0\u6587\u4EF6\uFF0C\u8BF7\u4F7F\u7528\u804A\u5929\u3002",
367
+ askBusy: "\u5F53\u524D\u56DE\u7B54\u51C6\u5907\u597D\u540E\u5373\u53EF\u63D0\u95EE\u3002",
368
+ askUnavailable: "\u804A\u5929\u76EE\u524D\u4E0D\u53EF\u7528\u3002",
369
+ gettingAnswer: "\u6B63\u5728\u83B7\u53D6\u56DE\u7B54\u2026",
370
+ startWhenReady: "\u51C6\u5907\u597D\u540E\u5F00\u59CB",
371
+ armed: "\u56DE\u7B54\u51C6\u5907\u597D\u540E\uFF0C\u865A\u62DF\u5F62\u8C61\u5C06\u5F00\u59CB\u3002",
372
+ cancel: "\u53D6\u6D88",
373
+ armedCancelled: "\u865A\u62DF\u5F62\u8C61\u672A\u542F\u52A8\u3002\u53EF\u518D\u6B21\u6309\u201C\u51C6\u5907\u597D\u540E\u5F00\u59CB\u201D\u3002",
374
+ notSpoken: "\u6B64\u56DE\u7B54\u4EE5\u6587\u5B57\u5F62\u5F0F\u4FDD\u7559\u5728\u804A\u5929\u4E2D\u3002\u865A\u62DF\u5F62\u8C61\u53EA\u6717\u8BFB\u901A\u8FC7\u5176\u8BED\u97F3\u68C0\u67E5\u7684\u56DE\u7B54\u3002",
375
+ answerFailed: "\u52A9\u624B\u65E0\u6CD5\u56DE\u7B54\u3002\u8BF7\u91CD\u65B0\u63D0\u95EE\u3002",
376
+ declined: "\u6CA1\u5173\u7CFB\u3002\u56DE\u7B54\u5728\u804A\u5929\u4E2D\u3002",
377
+ askHere: "\u5982\u9700\u542C\u53D6\u56DE\u7B54\uFF0C\u8BF7\u5728\u8FD9\u91CC\u63D0\u95EE\u3002",
378
+ newChatForAvatar: "\u4E3A\u865A\u62DF\u5F62\u8C61\u5F00\u59CB\u65B0\u804A\u5929",
379
+ newChatNote: "\u5F53\u524D\u804A\u5929\u4F1A\u4FDD\u7559\u5728\u5386\u53F2\u8BB0\u5F55\u4E2D\u3002",
380
+ newChatStarted: "\u5DF2\u4E3A\u865A\u62DF\u5F62\u8C61\u5F00\u59CB\u65B0\u7684\u804A\u5929\u3002",
381
+ readInChat: "\u5728\u804A\u5929\u4E2D\u67E5\u770B\u56DE\u7B54\u548C\u6765\u6E90",
382
+ 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",
383
+ needsAnswerAny: "\u5728\u8FD9\u91CC\u63D0\u95EE\uFF0C\u865A\u62DF\u5F62\u8C61\u4F1A\u6717\u8BFB\u56DE\u7B54\u3002",
384
+ 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",
385
+ notEligibleAny: "\u865A\u62DF\u5F62\u8C61\u65E0\u6CD5\u6717\u8BFB\u6B64\u56DE\u7B54\u3002\u56DE\u7B54\u548C\u6765\u6E90\u5728\u804A\u5929\u4E2D\u3002"
323
386
  };
324
387
  var HI = {
325
388
  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",
@@ -353,7 +416,32 @@ var HI = {
353
416
  paused: "\u0932\u093E\u0907\u0935 \u0905\u0935\u0924\u093E\u0930 \u0905\u092D\u0940 \u0930\u0941\u0915\u093E \u0939\u0941\u0906 \u0939\u0948\u0964",
354
417
  // TRANSLATION REVIEW PENDING (BOFF-9797): draft.
355
418
  notEligible: "\u0905\u0935\u0924\u093E\u0930 \u0938\u0941\u091D\u093E\u090F \u0917\u090F \u092A\u094D\u0930\u0936\u094D\u0928 \u0915\u093E \u092A\u0939\u0932\u093E \u0909\u0924\u094D\u0924\u0930 \u0939\u0940 \u092A\u0922\u093C\u0924\u093E \u0939\u0948, \u092A\u0902\u0926\u094D\u0930\u0939 \u092E\u093F\u0928\u091F \u0915\u0947 \u092D\u0940\u0924\u0930\u0964 \u0928\u0908 \u091A\u0948\u091F \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902 \u0914\u0930 \u0915\u094B\u0908 \u0938\u0941\u091D\u093E\u092F\u093E \u0917\u092F\u093E \u092A\u094D\u0930\u0936\u094D\u0928 \u091A\u0941\u0928\u0947\u0902\u0964",
356
- answerClaimed: "\u0905\u0935\u0924\u093E\u0930 \u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u092A\u0939\u0932\u0947 \u0939\u0940 \u092A\u0922\u093C \u091A\u0941\u0915\u093E \u0939\u0948\u0964"
419
+ answerClaimed: "\u0905\u0935\u0924\u093E\u0930 \u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u092A\u0939\u0932\u0947 \u0939\u0940 \u092A\u0922\u093C \u091A\u0941\u0915\u093E \u0939\u0948\u0964",
420
+ // TRANSLATION REVIEW PENDING (BOFF-9797): draft, the Avatar tab's question box.
421
+ askHeadline: "\u0915\u094B\u0908 \u092A\u094D\u0930\u0936\u094D\u0928 \u092A\u0942\u091B\u0947\u0902, \u0914\u0930 \u0905\u0935\u0924\u093E\u0930 \u0909\u0938\u0915\u093E \u0909\u0924\u094D\u0924\u0930 \u091C\u093C\u094B\u0930 \u0938\u0947 \u092A\u0922\u093C\u0947\u0917\u093E\u0964",
422
+ askLabel: "\u0905\u0935\u0924\u093E\u0930 \u0915\u0947 \u0932\u093F\u090F \u092A\u094D\u0930\u0936\u094D\u0928",
423
+ askPlaceholder: "\u0905\u0935\u0924\u093E\u0930 \u0938\u0947 \u092A\u0942\u091B\u0947\u0902\u2026",
424
+ askFooter: "\u0909\u0924\u094D\u0924\u0930 AI \u0926\u094D\u0935\u093E\u0930\u093E \u092C\u0928\u093E\u090F \u091C\u093E\u0924\u0947 \u0939\u0948\u0902 \u0914\u0930 \u0917\u0932\u0924 \u0939\u094B \u0938\u0915\u0924\u0947 \u0939\u0948\u0902\u0964 \u0928\u093F\u091C\u0940 \u091C\u093E\u0928\u0915\u093E\u0930\u0940 \u0936\u093E\u092E\u093F\u0932 \u0928 \u0915\u0930\u0947\u0902\u0964",
425
+ askNoFiles: "\u092B\u093C\u093E\u0907\u0932\u0947\u0902 \u091C\u094B\u0921\u093C\u0928\u0947 \u0915\u0947 \u0932\u093F\u090F \u091A\u0948\u091F \u0915\u093E \u0909\u092A\u092F\u094B\u0917 \u0915\u0930\u0947\u0902\u0964",
426
+ askBusy: "\u092E\u094C\u091C\u0942\u0926\u093E \u0909\u0924\u094D\u0924\u0930 \u0924\u0948\u092F\u093E\u0930 \u0939\u094B\u0928\u0947 \u092A\u0930 \u0906\u092A \u092A\u0942\u091B \u0938\u0915\u0924\u0947 \u0939\u0948\u0902\u0964",
427
+ askUnavailable: "\u091A\u0948\u091F \u0905\u092D\u0940 \u0909\u092A\u0932\u092C\u094D\u0927 \u0928\u0939\u0940\u0902 \u0939\u0948\u0964",
428
+ gettingAnswer: "\u0909\u0924\u094D\u0924\u0930 \u0932\u093E\u092F\u093E \u091C\u093E \u0930\u0939\u093E \u0939\u0948\u2026",
429
+ startWhenReady: "\u0924\u0948\u092F\u093E\u0930 \u0939\u094B\u0928\u0947 \u092A\u0930 \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902",
430
+ armed: "\u0909\u0924\u094D\u0924\u0930 \u0924\u0948\u092F\u093E\u0930 \u0939\u094B\u0924\u0947 \u0939\u0940 \u0905\u0935\u0924\u093E\u0930 \u0936\u0941\u0930\u0942 \u0939\u094B\u0917\u093E\u0964",
431
+ cancel: "\u0930\u0926\u094D\u0926 \u0915\u0930\u0947\u0902",
432
+ armedCancelled: "\u0905\u0935\u0924\u093E\u0930 \u0936\u0941\u0930\u0942 \u0928\u0939\u0940\u0902 \u0939\u0941\u0906\u0964 \u092B\u093F\u0930 \u0938\u0947 \u0915\u094B\u0936\u093F\u0936 \u0915\u0930\u0928\u0947 \u0915\u0947 \u0932\u093F\u090F \u201C\u0924\u0948\u092F\u093E\u0930 \u0939\u094B\u0928\u0947 \u092A\u0930 \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902\u201D \u0926\u092C\u093E\u090F\u0901\u0964",
433
+ notSpoken: "\u092F\u0939 \u0909\u0924\u094D\u0924\u0930 \u091A\u0948\u091F \u092E\u0947\u0902 \u091F\u0947\u0915\u094D\u0938\u094D\u091F \u0915\u0947 \u0930\u0942\u092A \u092E\u0947\u0902 \u0930\u0939\u0947\u0917\u093E\u0964 \u0905\u0935\u0924\u093E\u0930 \u0915\u0947\u0935\u0932 \u0935\u0939\u0940 \u0909\u0924\u094D\u0924\u0930 \u092A\u0922\u093C\u0924\u093E \u0939\u0948 \u091C\u094B \u0909\u0938\u0915\u0940 \u0935\u093E\u0923\u0940 \u091C\u093E\u0901\u091A \u092E\u0947\u0902 \u092A\u093E\u0938 \u0939\u094B\u0924\u0947 \u0939\u0948\u0902\u0964",
434
+ answerFailed: "\u0938\u0939\u093E\u092F\u0915 \u0909\u0924\u094D\u0924\u0930 \u0928\u0939\u0940\u0902 \u0926\u0947 \u0938\u0915\u093E\u0964 \u092B\u093F\u0930 \u0938\u0947 \u092A\u0942\u091B\u0915\u0930 \u0926\u0947\u0916\u0947\u0902\u0964",
435
+ declined: "\u0915\u094B\u0908 \u092C\u093E\u0924 \u0928\u0939\u0940\u0902\u0964 \u0909\u0924\u094D\u0924\u0930 \u091A\u0948\u091F \u092E\u0947\u0902 \u0939\u0948\u0964",
436
+ askHere: "\u0909\u0924\u094D\u0924\u0930 \u0938\u0941\u0928\u0928\u0947 \u0915\u0947 \u0932\u093F\u090F, \u092F\u0939\u093E\u0901 \u092A\u0942\u091B\u0947\u0902\u0964",
437
+ newChatForAvatar: "\u0905\u0935\u0924\u093E\u0930 \u0915\u0947 \u0932\u093F\u090F \u0928\u0908 \u091A\u0948\u091F",
438
+ newChatNote: "\u0906\u092A\u0915\u0940 \u092E\u094C\u091C\u0942\u0926\u093E \u091A\u0948\u091F \u0907\u0924\u093F\u0939\u093E\u0938 \u092E\u0947\u0902 \u0930\u0939\u0947\u0917\u0940\u0964",
439
+ newChatStarted: "\u0905\u0935\u0924\u093E\u0930 \u0915\u0947 \u0932\u093F\u090F \u0928\u0908 \u091A\u0948\u091F \u0936\u0941\u0930\u0942 \u0915\u0940 \u0917\u0908\u0964",
440
+ readInChat: "\u0909\u0924\u094D\u0924\u0930 \u0914\u0930 \u0938\u094D\u0930\u094B\u0924 \u091A\u0948\u091F \u092E\u0947\u0902 \u092A\u0922\u093C\u0947\u0902",
441
+ 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",
442
+ 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",
443
+ 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",
444
+ 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"
357
445
  };
358
446
  var ES = {
359
447
  checking: "Comprobando si el avatar en vivo est\xE1 disponible\u2026",
@@ -387,7 +475,32 @@ var ES = {
387
475
  paused: "El avatar en vivo est\xE1 en pausa en este momento.",
388
476
  // TRANSLATION REVIEW PENDING (BOFF-9797): draft.
389
477
  notEligible: "El avatar lee la primera respuesta a una pregunta sugerida, en un plazo de quince minutos. Inicia un chat nuevo y elige una pregunta sugerida.",
390
- answerClaimed: "El avatar ya ha le\xEDdo esta respuesta."
478
+ answerClaimed: "El avatar ya ha le\xEDdo esta respuesta.",
479
+ // TRANSLATION REVIEW PENDING (BOFF-9797): draft, the Avatar tab's question box.
480
+ askHeadline: "Haz una pregunta y el avatar leer\xE1 la respuesta en voz alta.",
481
+ askLabel: "Pregunta para el avatar",
482
+ askPlaceholder: "Pregunta al avatar\u2026",
483
+ askFooter: "Las respuestas las genera la IA y pueden contener errores. No incluyas datos personales.",
484
+ askNoFiles: "Para adjuntar archivos, usa el chat.",
485
+ askBusy: "Podr\xE1s preguntar cuando la respuesta actual est\xE9 lista.",
486
+ askUnavailable: "El chat no est\xE1 disponible en este momento.",
487
+ gettingAnswer: "Obteniendo la respuesta\u2026",
488
+ startWhenReady: "Iniciar cuando est\xE9 lista",
489
+ armed: "El avatar empezar\xE1 cuando la respuesta est\xE9 lista.",
490
+ cancel: "Cancelar",
491
+ armedCancelled: "El avatar no se ha iniciado. Pulsa Iniciar cuando est\xE9 lista para volver a intentarlo.",
492
+ notSpoken: "Esta respuesta se queda en el chat como texto. El avatar solo lee respuestas que superan sus comprobaciones de voz.",
493
+ answerFailed: "El asistente no pudo responder. Vuelve a preguntar.",
494
+ declined: "De acuerdo. La respuesta est\xE1 en el chat.",
495
+ askHere: "Para escuchar una respuesta, pregunta aqu\xED.",
496
+ newChatForAvatar: "Chat nuevo para el avatar",
497
+ newChatNote: "Tu chat actual se queda en el historial.",
498
+ newChatStarted: "Se ha iniciado un chat nuevo para el avatar.",
499
+ readInChat: "Leer la respuesta y las fuentes en el chat",
500
+ usedToday: "Ya has usado la sesi\xF3n de avatar de hoy. Las respuestas se mostrar\xE1n aqu\xED como texto.",
501
+ needsAnswerAny: "Haz una pregunta aqu\xED y el avatar leer\xE1 la respuesta en voz alta.",
502
+ 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.",
503
+ notEligibleAny: "El avatar no puede leer esta respuesta en voz alta. La respuesta y las fuentes est\xE1n en el chat."
391
504
  };
392
505
  var AR = {
393
506
  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",
@@ -421,7 +534,32 @@ var AR = {
421
534
  paused: "\u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0645\u0628\u0627\u0634\u0631 \u0645\u062A\u0648\u0642\u0641 \u0645\u0624\u0642\u062A\u064B\u0627 \u0627\u0644\u0622\u0646.",
422
535
  // TRANSLATION REVIEW PENDING (BOFF-9797): draft.
423
536
  notEligible: "\u064A\u0642\u0631\u0623 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0627\u0644\u0623\u0648\u0644\u0649 \u0639\u0646 \u0633\u0624\u0627\u0644 \u0645\u0642\u062A\u0631\u062D\u060C \u062E\u0644\u0627\u0644 \u062E\u0645\u0633 \u0639\u0634\u0631\u0629 \u062F\u0642\u064A\u0642\u0629. \u0627\u0628\u062F\u0623 \u062F\u0631\u062F\u0634\u0629 \u062C\u062F\u064A\u062F\u0629 \u0648\u0627\u062E\u062A\u0631 \u0633\u0624\u0627\u0644\u064B\u0627 \u0645\u0642\u062A\u0631\u062D\u064B\u0627.",
424
- answerClaimed: "\u0642\u0631\u0623 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0645\u0646 \u0642\u0628\u0644."
537
+ answerClaimed: "\u0642\u0631\u0623 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0645\u0646 \u0642\u0628\u0644.",
538
+ // TRANSLATION REVIEW PENDING (BOFF-9797): draft, the Avatar tab's question box.
539
+ askHeadline: "\u0627\u0637\u0631\u062D \u0633\u0624\u0627\u0644\u064B\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.",
540
+ askLabel: "\u0633\u0624\u0627\u0644 \u0644\u0644\u0623\u0641\u0627\u062A\u0627\u0631",
541
+ askPlaceholder: "\u0627\u0633\u0623\u0644 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631\u2026",
542
+ askFooter: "\u0627\u0644\u0625\u062C\u0627\u0628\u0627\u062A \u0645\u064F\u0648\u0644\u064E\u0651\u062F\u0629 \u0628\u0627\u0644\u0630\u0643\u0627\u0621 \u0627\u0644\u0627\u0635\u0637\u0646\u0627\u0639\u064A \u0648\u0642\u062F \u062A\u0643\u0648\u0646 \u062E\u0627\u0637\u0626\u0629. \u0644\u0627 \u062A\u0636\u0645\u0651\u0646 \u0628\u064A\u0627\u0646\u0627\u062A \u0634\u062E\u0635\u064A\u0629.",
543
+ askNoFiles: "\u0644\u0625\u0631\u0641\u0627\u0642 \u0645\u0644\u0641\u0627\u062A\u060C \u0627\u0633\u062A\u062E\u062F\u0645 \u0627\u0644\u062F\u0631\u062F\u0634\u0629.",
544
+ askBusy: "\u064A\u0645\u0643\u0646\u0643 \u0627\u0644\u0633\u0624\u0627\u0644 \u0639\u0646\u062F\u0645\u0627 \u062A\u0635\u0628\u062D \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0627\u0644\u062D\u0627\u0644\u064A\u0629 \u062C\u0627\u0647\u0632\u0629.",
545
+ askUnavailable: "\u0627\u0644\u062F\u0631\u062F\u0634\u0629 \u063A\u064A\u0631 \u0645\u062A\u0627\u062D\u0629 \u0627\u0644\u0622\u0646.",
546
+ gettingAnswer: "\u062C\u0627\u0631\u064D \u0627\u0644\u062D\u0635\u0648\u0644 \u0639\u0644\u0649 \u0627\u0644\u0625\u062C\u0627\u0628\u0629\u2026",
547
+ startWhenReady: "\u0627\u0644\u0628\u062F\u0621 \u0639\u0646\u062F \u0627\u0644\u062C\u0627\u0647\u0632\u064A\u0629",
548
+ armed: "\u0633\u064A\u0628\u062F\u0623 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0639\u0646\u062F\u0645\u0627 \u062A\u0635\u0628\u062D \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u062C\u0627\u0647\u0632\u0629.",
549
+ cancel: "\u0625\u0644\u063A\u0627\u0621",
550
+ armedCancelled: "\u0644\u0645 \u064A\u0628\u062F\u0623 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631. \u0627\u0636\u063A\u0637 \u0639\u0644\u0649 \u0627\u0644\u0628\u062F\u0621 \u0639\u0646\u062F \u0627\u0644\u062C\u0627\u0647\u0632\u064A\u0629 \u0644\u0644\u0645\u062D\u0627\u0648\u0644\u0629 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649.",
551
+ notSpoken: "\u062A\u0628\u0642\u0649 \u0647\u0630\u0647 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629 \u0646\u0635\u064B\u0627. \u064A\u0642\u0631\u0623 \u0627\u0644\u0623\u0641\u0627\u062A\u0627\u0631 \u0641\u0642\u0637 \u0627\u0644\u0625\u062C\u0627\u0628\u0627\u062A \u0627\u0644\u062A\u064A \u062A\u062C\u062A\u0627\u0632 \u0641\u062D\u0648\u0635 \u0627\u0644\u0646\u0637\u0642.",
552
+ answerFailed: "\u062A\u0639\u0630\u0651\u0631 \u0639\u0644\u0649 \u0627\u0644\u0645\u0633\u0627\u0639\u062F \u0627\u0644\u0625\u062C\u0627\u0628\u0629. \u062D\u0627\u0648\u0644 \u0627\u0644\u0633\u0624\u0627\u0644 \u0645\u0631\u0629 \u0623\u062E\u0631\u0649.",
553
+ declined: "\u0644\u0627 \u0628\u0623\u0633. \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0645\u0648\u062C\u0648\u062F\u0629 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629.",
554
+ askHere: "\u0644\u0633\u0645\u0627\u0639 \u0625\u062C\u0627\u0628\u0629\u060C \u0627\u0633\u0623\u0644 \u0647\u0646\u0627.",
555
+ newChatForAvatar: "\u062F\u0631\u062F\u0634\u0629 \u062C\u062F\u064A\u062F\u0629 \u0644\u0644\u0623\u0641\u0627\u062A\u0627\u0631",
556
+ newChatNote: "\u062A\u0628\u0642\u0649 \u062F\u0631\u062F\u0634\u062A\u0643 \u0627\u0644\u062D\u0627\u0644\u064A\u0629 \u0641\u064A \u0627\u0644\u0633\u062C\u0644.",
557
+ newChatStarted: "\u0628\u062F\u0623\u062A \u062F\u0631\u062F\u0634\u0629 \u062C\u062F\u064A\u062F\u0629 \u0644\u0644\u0623\u0641\u0627\u062A\u0627\u0631.",
558
+ readInChat: "\u0627\u0642\u0631\u0623 \u0627\u0644\u0625\u062C\u0627\u0628\u0629 \u0648\u0627\u0644\u0645\u0635\u0627\u062F\u0631 \u0641\u064A \u0627\u0644\u062F\u0631\u062F\u0634\u0629",
559
+ 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.",
560
+ 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.",
561
+ 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.",
562
+ 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."
425
563
  };
426
564
  var TA = {
427
565
  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",
@@ -455,7 +593,32 @@ var TA = {
455
593
  paused: "\u0BA8\u0BC7\u0BB0\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0B87\u0BAA\u0BCD\u0BAA\u0BCB\u0BA4\u0BC1 \u0B87\u0B9F\u0BC8\u0BA8\u0BBF\u0BB1\u0BC1\u0BA4\u0BCD\u0BA4\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F\u0BC1\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1.",
456
594
  // TRANSLATION REVIEW PENDING (BOFF-9797): draft.
457
595
  notEligible: "\u0BAA\u0BB0\u0BBF\u0BA8\u0BCD\u0BA4\u0BC1\u0BB0\u0BC8\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F \u0B95\u0BC7\u0BB3\u0BCD\u0BB5\u0BBF\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAE\u0BC1\u0BA4\u0BB2\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0BAE\u0B9F\u0BCD\u0B9F\u0BC1\u0BAE\u0BC7, \u0BAA\u0BA4\u0BBF\u0BA9\u0BC8\u0BA8\u0BCD\u0BA4\u0BC1 \u0BA8\u0BBF\u0BAE\u0BBF\u0B9F\u0B99\u0BCD\u0B95\u0BB3\u0BC1\u0B95\u0BCD\u0B95\u0BC1\u0BB3\u0BCD, \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95\u0BC1\u0BAE\u0BCD. \u0BAA\u0BC1\u0BA4\u0BBF\u0BAF \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BC8\u0BA4\u0BCD \u0BA4\u0BCA\u0B9F\u0B99\u0BCD\u0B95\u0BBF, \u0BAA\u0BB0\u0BBF\u0BA8\u0BCD\u0BA4\u0BC1\u0BB0\u0BC8\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F \u0B92\u0BB0\u0BC1 \u0B95\u0BC7\u0BB3\u0BCD\u0BB5\u0BBF\u0BAF\u0BC8\u0BA4\u0BCD \u0BA4\u0BC7\u0BB0\u0BCD\u0BA8\u0BCD\u0BA4\u0BC6\u0B9F\u0BC1\u0B95\u0BCD\u0B95\u0BB5\u0BC1\u0BAE\u0BCD.",
458
- answerClaimed: "\u0B87\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0B8F\u0BB1\u0BCD\u0B95\u0BC6\u0BA9\u0BB5\u0BC7 \u0BAA\u0B9F\u0BBF\u0BA4\u0BCD\u0BA4\u0BC1\u0BB5\u0BBF\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1."
596
+ answerClaimed: "\u0B87\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BC8 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0B8F\u0BB1\u0BCD\u0B95\u0BC6\u0BA9\u0BB5\u0BC7 \u0BAA\u0B9F\u0BBF\u0BA4\u0BCD\u0BA4\u0BC1\u0BB5\u0BBF\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1.",
597
+ // TRANSLATION REVIEW PENDING (BOFF-9797): draft, the Avatar tab's question box.
598
+ askHeadline: "\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.",
599
+ askLabel: "\u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BC1\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0B95\u0BC7\u0BB3\u0BCD\u0BB5\u0BBF",
600
+ askPlaceholder: "\u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BBF\u0B9F\u0BAE\u0BCD \u0B95\u0BC7\u0BB3\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD\u2026",
601
+ askFooter: "\u0BAA\u0BA4\u0BBF\u0BB2\u0BCD\u0B95\u0BB3\u0BCD AI \u0B89\u0BB0\u0BC1\u0BB5\u0BBE\u0B95\u0BCD\u0B95\u0BBF\u0BAF\u0BB5\u0BC8, \u0BA4\u0BB5\u0BB1\u0BBE\u0B95 \u0B87\u0BB0\u0BC1\u0B95\u0BCD\u0B95\u0BB2\u0BBE\u0BAE\u0BCD. \u0BA4\u0BA9\u0BBF\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F \u0BB5\u0BBF\u0BB5\u0BB0\u0B99\u0BCD\u0B95\u0BB3\u0BC8\u0B9A\u0BCD \u0B9A\u0BC7\u0BB0\u0BCD\u0B95\u0BCD\u0B95 \u0BB5\u0BC7\u0BA3\u0BCD\u0B9F\u0BBE\u0BAE\u0BCD.",
602
+ askNoFiles: "\u0B95\u0BCB\u0BAA\u0BCD\u0BAA\u0BC1\u0B95\u0BB3\u0BC8 \u0B87\u0BA3\u0BC8\u0B95\u0BCD\u0B95, \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BC8\u0BAA\u0BCD \u0BAA\u0BAF\u0BA9\u0BCD\u0BAA\u0B9F\u0BC1\u0BA4\u0BCD\u0BA4\u0BB5\u0BC1\u0BAE\u0BCD.",
603
+ askBusy: "\u0BA4\u0BB1\u0BCD\u0BAA\u0BCB\u0BA4\u0BC8\u0BAF \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0BA4\u0BAF\u0BBE\u0BB0\u0BBE\u0BA9\u0BA4\u0BC1\u0BAE\u0BCD \u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0B95\u0BC7\u0B9F\u0BCD\u0B95\u0BB2\u0BBE\u0BAE\u0BCD.",
604
+ askUnavailable: "\u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8 \u0B87\u0BAA\u0BCD\u0BAA\u0BCB\u0BA4\u0BC1 \u0B95\u0BBF\u0B9F\u0BC8\u0B95\u0BCD\u0B95\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8.",
605
+ gettingAnswer: "\u0BAA\u0BA4\u0BBF\u0BB2\u0BC8\u0BAA\u0BCD \u0BAA\u0BC6\u0BB1\u0BC1\u0B95\u0BBF\u0BB1\u0BA4\u0BC1\u2026",
606
+ startWhenReady: "\u0BA4\u0BAF\u0BBE\u0BB0\u0BBE\u0BA9\u0BA4\u0BC1\u0BAE\u0BCD \u0BA4\u0BCA\u0B9F\u0B99\u0BCD\u0B95\u0BC1",
607
+ armed: "\u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0BA4\u0BAF\u0BBE\u0BB0\u0BBE\u0BA9\u0BA4\u0BC1\u0BAE\u0BCD \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BA4\u0BCA\u0B9F\u0B99\u0BCD\u0B95\u0BC1\u0BAE\u0BCD.",
608
+ cancel: "\u0BB0\u0BA4\u0BCD\u0BA4\u0BC1\u0B9A\u0BC6\u0BAF\u0BCD",
609
+ armedCancelled: "\u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BA4\u0BCA\u0B9F\u0B99\u0BCD\u0B95\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8. \u0BAE\u0BC0\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD \u0BAE\u0BC1\u0BAF\u0BB2, \u0BA4\u0BAF\u0BBE\u0BB0\u0BBE\u0BA9\u0BA4\u0BC1\u0BAE\u0BCD \u0BA4\u0BCA\u0B9F\u0B99\u0BCD\u0B95\u0BC1 \u0B8E\u0BA9\u0BCD\u0BAA\u0BA4\u0BC8 \u0B85\u0BB4\u0BC1\u0BA4\u0BCD\u0BA4\u0BB5\u0BC1\u0BAE\u0BCD.",
610
+ notSpoken: "\u0B87\u0BA8\u0BCD\u0BA4\u0BAA\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0B89\u0BB0\u0BC8\u0BAF\u0BBE\u0B95\u0BB5\u0BC7 \u0B87\u0BB0\u0BC1\u0B95\u0BCD\u0B95\u0BC1\u0BAE\u0BCD. \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\u0BC1\u0BAE\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD\u0B95\u0BB3\u0BC8 \u0BAE\u0B9F\u0BCD\u0B9F\u0BC1\u0BAE\u0BC7 \u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95\u0BC1\u0BAE\u0BCD.",
611
+ answerFailed: "\u0B89\u0BA4\u0BB5\u0BBF\u0BAF\u0BBE\u0BB3\u0BB0\u0BBE\u0BB2\u0BCD \u0BAA\u0BA4\u0BBF\u0BB2\u0BB3\u0BBF\u0B95\u0BCD\u0B95 \u0BAE\u0BC1\u0B9F\u0BBF\u0BAF\u0BB5\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8. \u0BAE\u0BC0\u0BA3\u0BCD\u0B9F\u0BC1\u0BAE\u0BCD \u0B95\u0BC7\u0B9F\u0BCD\u0B9F\u0BC1\u0BAA\u0BCD \u0BAA\u0BBE\u0BB0\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD.",
612
+ declined: "\u0BAA\u0BB0\u0BB5\u0BBE\u0BAF\u0BBF\u0BB2\u0BCD\u0BB2\u0BC8. \u0BAA\u0BA4\u0BBF\u0BB2\u0BCD \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0B89\u0BB3\u0BCD\u0BB3\u0BA4\u0BC1.",
613
+ askHere: "\u0BAA\u0BA4\u0BBF\u0BB2\u0BC8\u0B95\u0BCD \u0B95\u0BC7\u0B9F\u0BCD\u0B95, \u0B87\u0B99\u0BCD\u0B95\u0BC7 \u0B95\u0BC7\u0BB3\u0BC1\u0B99\u0BCD\u0B95\u0BB3\u0BCD.",
614
+ newChatForAvatar: "\u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BC1\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAA\u0BC1\u0BA4\u0BBF\u0BAF \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8",
615
+ newChatNote: "\u0B89\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BA4\u0BB1\u0BCD\u0BAA\u0BCB\u0BA4\u0BC8\u0BAF \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8 \u0BB5\u0BB0\u0BB2\u0BBE\u0BB1\u0BCD\u0BB1\u0BBF\u0BB2\u0BCD \u0B87\u0BB0\u0BC1\u0B95\u0BCD\u0B95\u0BC1\u0BAE\u0BCD.",
616
+ newChatStarted: "\u0B85\u0BB5\u0BA4\u0BBE\u0BB0\u0BC1\u0B95\u0BCD\u0B95\u0BBE\u0B95\u0BAA\u0BCD \u0BAA\u0BC1\u0BA4\u0BBF\u0BAF \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8 \u0BA4\u0BCA\u0B9F\u0B99\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1.",
617
+ readInChat: "\u0BAA\u0BA4\u0BBF\u0BB2\u0BC8\u0BAF\u0BC1\u0BAE\u0BCD \u0B86\u0BA4\u0BBE\u0BB0\u0B99\u0BCD\u0B95\u0BB3\u0BC8\u0BAF\u0BC1\u0BAE\u0BCD \u0B85\u0BB0\u0B9F\u0BCD\u0B9F\u0BC8\u0BAF\u0BBF\u0BB2\u0BCD \u0BAA\u0B9F\u0BBF\u0B95\u0BCD\u0B95\u0BB5\u0BC1\u0BAE\u0BCD",
618
+ 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.",
619
+ 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.",
620
+ 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.",
621
+ 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."
459
622
  };
460
623
  var STRINGS = {
461
624
  en: EN,
@@ -479,7 +642,8 @@ var CONSENT = /* @__PURE__ */ new Map([
479
642
  processing: "When you start, Cloudflare checks for bots. The answer text goes to Google to create the voice. Your browser connects to LiveKit, which sees your IP address. LiveKit fetches our temporary audio file and passes the voice to Protoface, which makes the avatar video streamed through LiveKit.",
480
643
  storage: "Your microphone and camera are not used. We do not record the live session. We temporarily store the generated voice file and keep a record of this consent. The privacy notice explains our safeguards, storage periods and the providers' processing. For people aged eighteen and over.",
481
644
  privacyLink: "How we handle this",
482
- botCheck: "turnstile"
645
+ botCheck: "turnstile",
646
+ questions: "suggested"
483
647
  }
484
648
  }
485
649
  ],
@@ -494,7 +658,8 @@ var CONSENT = /* @__PURE__ */ new Map([
494
658
  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.",
495
659
  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.",
496
660
  privacyLink: "How we handle this",
497
- botCheck: "turnstile"
661
+ botCheck: "turnstile",
662
+ questions: "suggested"
498
663
  }
499
664
  }
500
665
  ],
@@ -510,7 +675,8 @@ var CONSENT = /* @__PURE__ */ new Map([
510
675
  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.",
511
676
  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.",
512
677
  privacyLink: "How we handle this",
513
- botCheck: "turnstile"
678
+ botCheck: "turnstile",
679
+ questions: "suggested"
514
680
  }
515
681
  }
516
682
  ],
@@ -526,7 +692,44 @@ var CONSENT = /* @__PURE__ */ new Map([
526
692
  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.",
527
693
  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.",
528
694
  privacyLink: "How we handle this",
529
- botCheck: "off"
695
+ botCheck: "off",
696
+ questions: "suggested"
697
+ }
698
+ }
699
+ ],
700
+ [
701
+ // The Avatar tab's own question box (contract v1.3, `questions: "any"`): the first answer in a new chat started in
702
+ // the Avatar tab is read whether the visitor chose a suggested question or typed their own, so the intro says so and
703
+ // warns against personal details in a typed one. Product-neutral, with the bot check: one notice and one digest for
704
+ // every site (gitops docs/avatar-web/en-web-consent-2026-11-05.txt).
705
+ "web-consent-2026-11-05",
706
+ {
707
+ en: {
708
+ title: "Live AI avatar (beta)",
709
+ introProduct: null,
710
+ intro: "This is an AI avatar, not a real person. It reads aloud, for up to a minute, the first answer in a new chat started in the Avatar tab, provided it passes the speech checks, whether you chose a suggested question or typed your own. The answer is AI-generated and can be wrong. Don't put personal details in a question you type.",
711
+ 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.",
712
+ 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.",
713
+ privacyLink: "How we handle this",
714
+ botCheck: "turnstile",
715
+ questions: "any"
716
+ }
717
+ }
718
+ ],
719
+ [
720
+ // The words of web-consent-2026-11-05 without "When you start, Cloudflare checks for bots.", for a server that runs
721
+ // no bot check (gitops docs/avatar-web/en-web-consent-2026-11-06.txt).
722
+ "web-consent-2026-11-06",
723
+ {
724
+ en: {
725
+ title: "Live AI avatar (beta)",
726
+ introProduct: null,
727
+ intro: "This is an AI avatar, not a real person. It reads aloud, for up to a minute, the first answer in a new chat started in the Avatar tab, provided it passes the speech checks, whether you chose a suggested question or typed your own. The answer is AI-generated and can be wrong. Don't put personal details in a question you type.",
728
+ 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.",
729
+ 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.",
730
+ privacyLink: "How we handle this",
731
+ botCheck: "off",
732
+ questions: "any"
530
733
  }
531
734
  }
532
735
  ]
@@ -657,6 +860,9 @@ var CLOCK_TOLERANCE_MS = 5e3;
657
860
  var SERVICE_MAX_AGE_MS = 15 * 6e4;
658
861
  var ANSWER_OFFER_MS = SERVICE_MAX_AGE_MS - 6e4;
659
862
  var MAX_TIMER_MS = 2147483647;
863
+ var ASK_MAX_LENGTH = 300;
864
+ var MAX_REFERENCES = 64;
865
+ var USED_ON_KEY = "boff.explore.avatarUsedOn";
660
866
  var PRIMARY = '[data-avatar-primary], [data-boff-explore="panel-continue-chat"]';
661
867
  var IDLE = { name: "idle" };
662
868
  var INERT2 = { name: "inert" };
@@ -669,12 +875,26 @@ var REFUSAL_TEXT = {
669
875
  not_eligible: "notEligible",
670
876
  answer_claimed: "answerClaimed"
671
877
  };
672
- var WAITING = { answerId: null, startOver: false, offerUntil: NaN };
673
- var START_OVER = {
878
+ var EMPTY = {
879
+ kind: "empty",
674
880
  answerId: null,
675
- startOver: true,
676
- offerUntil: NaN
881
+ startOver: false,
882
+ offerUntil: NaN,
883
+ question: null,
884
+ pendingAnswerId: null,
885
+ askedHere: false
886
+ };
887
+ var START_OVER = { ...EMPTY, kind: "start-over", startOver: true };
888
+ var SUGGESTED_RULES = {
889
+ any: false,
890
+ askedHere: null,
891
+ token: null,
892
+ inFlight: false
677
893
  };
894
+ function isAskedHere(question, rules) {
895
+ const asked = rules.askedHere;
896
+ return rules.any && asked !== null && question.id === asked.messageId && asked.token === rules.token;
897
+ }
678
898
  function createdAtOf(message) {
679
899
  const raw = message.createdAt;
680
900
  return typeof raw === "string" && raw !== "" ? Date.parse(raw) : NaN;
@@ -682,25 +902,110 @@ function createdAtOf(message) {
682
902
  function offeredAt(exchange, now) {
683
903
  return exchange.answerId !== null && !(now < exchange.offerUntil) ? START_OVER : exchange;
684
904
  }
685
- function firstExchange(messages) {
686
- if (!messages || messages.length === 0) return WAITING;
905
+ function readable(question, answer) {
906
+ const references = answer.references?.length ?? 0;
907
+ return answer.answered === true && references >= 1 && references <= MAX_REFERENCES && (question.attachments?.length ?? 0) === 0;
908
+ }
909
+ function firstExchange(messages, rules = SUGGESTED_RULES) {
910
+ if (!messages || messages.length === 0) return EMPTY;
687
911
  if (messages.length > 2) return START_OVER;
688
912
  const [question, answer] = messages;
689
913
  if (question?.role !== "USER") return START_OVER;
690
- if (answer === void 0) return WAITING;
914
+ const first = { question, askedHere: isAskedHere(question, rules) };
915
+ if (answer === void 0) {
916
+ if (rules.inFlight) return { ...EMPTY, ...first, kind: "asking" };
917
+ return rules.any ? { ...START_OVER, ...first, kind: "failed" } : EMPTY;
918
+ }
691
919
  if (answer.role !== "ASSISTANT") return START_OVER;
692
920
  if (answer.status === "PENDING" || answer.status === "RUNNING") {
693
- return WAITING;
921
+ return {
922
+ ...EMPTY,
923
+ ...first,
924
+ kind: "asking",
925
+ pendingAnswerId: isAvatarMessageId(answer.id) ? answer.id : null
926
+ };
927
+ }
928
+ if (rules.any && answer.status === "FAILED") {
929
+ return { ...START_OVER, ...first, kind: "failed" };
694
930
  }
695
931
  if (answer.status !== "COMPLETED" || answer.content.trim() === "" || !isAvatarMessageId(answer.id)) {
696
932
  return START_OVER;
697
933
  }
934
+ if (rules.any && !first.askedHere) {
935
+ return { ...START_OVER, ...first, kind: "ask-here" };
936
+ }
937
+ if (rules.any && !readable(question, answer)) {
938
+ return { ...START_OVER, ...first, kind: "text-only" };
939
+ }
698
940
  return {
941
+ ...EMPTY,
942
+ ...first,
943
+ kind: "answer",
699
944
  answerId: answer.id,
700
- startOver: false,
701
945
  offerUntil: createdAtOf(answer) + ANSWER_OFFER_MS
702
946
  };
703
947
  }
948
+ function inFlight(conversation) {
949
+ return conversation?.phase === "sending" || conversation?.phase === "streaming";
950
+ }
951
+ function utcToday() {
952
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
953
+ }
954
+ function usedTodayHere() {
955
+ try {
956
+ return window.localStorage.getItem(USED_ON_KEY) === utcToday();
957
+ } catch {
958
+ return false;
959
+ }
960
+ }
961
+ function noteUsedToday() {
962
+ try {
963
+ window.localStorage.setItem(USED_ON_KEY, utcToday());
964
+ } catch {
965
+ }
966
+ }
967
+ var silence = null;
968
+ function silentWav() {
969
+ if (silence !== null) return silence;
970
+ const rate = 8e3;
971
+ const samples = rate / 10;
972
+ const bytes = new Uint8Array(44 + samples);
973
+ const view = new DataView(bytes.buffer);
974
+ const text = (at, value) => {
975
+ for (let i = 0; i < value.length; i += 1) {
976
+ bytes[at + i] = value.charCodeAt(i);
977
+ }
978
+ };
979
+ text(0, "RIFF");
980
+ view.setUint32(4, 36 + samples, true);
981
+ text(8, "WAVE");
982
+ text(12, "fmt ");
983
+ view.setUint32(16, 16, true);
984
+ view.setUint16(20, 1, true);
985
+ view.setUint16(22, 1, true);
986
+ view.setUint32(24, rate, true);
987
+ view.setUint32(28, rate, true);
988
+ view.setUint16(32, 1, true);
989
+ view.setUint16(34, 8, true);
990
+ text(36, "data");
991
+ view.setUint32(40, samples, true);
992
+ bytes.fill(128, 44);
993
+ let binary = "";
994
+ for (const byte of bytes) binary += String.fromCharCode(byte);
995
+ silence = `data:audio/wav;base64,${btoa(binary)}`;
996
+ return silence;
997
+ }
998
+ function unlockSound(audio) {
999
+ if (audio === null || audio.srcObject) return;
1000
+ try {
1001
+ audio.src = silentWav();
1002
+ void Promise.resolve(audio.play()).then(
1003
+ () => audio.pause(),
1004
+ () => void 0
1005
+ );
1006
+ } catch {
1007
+ }
1008
+ }
704
1009
  function offering(offer, messageId, claimed) {
705
1010
  return claimed.has(messageId) ? { name: "refused", reason: "answer_claimed" } : { name: "consent", offer, messageId };
706
1011
  }
@@ -719,13 +1024,20 @@ function clock(seconds) {
719
1024
  function AvatarLiveView({
720
1025
  live,
721
1026
  active,
1027
+ open,
722
1028
  locale,
723
1029
  productName,
724
1030
  still,
725
1031
  fallback,
726
1032
  onContinueInChat,
1033
+ onVoiceHold,
727
1034
  continueInChat,
728
1035
  newChat,
1036
+ send,
1037
+ tryAsking,
1038
+ prompts,
1039
+ touchFirst,
1040
+ currentPath,
729
1041
  off,
730
1042
  base,
731
1043
  headers,
@@ -734,18 +1046,56 @@ function AvatarLiveView({
734
1046
  const S = avatarLiveStrings(locale);
735
1047
  const uid = useId();
736
1048
  const consentTitleId = `${uid}-consent`;
737
- const exchange = firstExchange(conversation?.messages);
1049
+ const [questions, setQuestions] = useState("suggested");
1050
+ const questionsRef = useRef("suggested");
1051
+ const askedHereRef = useRef(null);
1052
+ const askSeqRef = useRef(0);
1053
+ const [, setAskedHereTick] = useState(0);
1054
+ const setAskedHere = useCallback((next) => {
1055
+ if (next === null) {
1056
+ askSeqRef.current += 1;
1057
+ if (askedHereRef.current === null) return;
1058
+ }
1059
+ askedHereRef.current = next;
1060
+ setAskedHereTick((tick) => tick + 1);
1061
+ }, []);
1062
+ const token = conversation?.getConversationToken() ?? null;
1063
+ const exchange = firstExchange(conversation?.messages, {
1064
+ any: questions === "any",
1065
+ askedHere: askedHereRef.current,
1066
+ token,
1067
+ inFlight: inFlight(conversation)
1068
+ });
738
1069
  const [ageTick, setAgeTick] = useState(0);
739
- const { answerId, startOver } = offeredAt(exchange, Date.now());
1070
+ const shown = offeredAt(exchange, Date.now());
1071
+ const { answerId, startOver } = shown;
740
1072
  const offerUntil = answerId === null ? null : exchange.offerUntil;
741
1073
  const [phase, setPhase] = useState(IDLE);
742
1074
  const [blocked, setBlocked] = useState(false);
743
1075
  const [now, setNow] = useState(() => Date.now());
1076
+ const [draft, setDraft] = useState("");
1077
+ const chatMaxLength = conversation?.catalog.limits.maxMessageLength;
1078
+ const askMaxLength = chatMaxLength !== void 0 && Number.isFinite(chatMaxLength) && chatMaxLength >= 1 ? Math.min(ASK_MAX_LENGTH, Math.floor(chatMaxLength)) : ASK_MAX_LENGTH;
1079
+ const [note, setNote] = useState(null);
1080
+ const [usedToday, setUsedToday] = useState(false);
1081
+ const showing = active && !off;
1082
+ const behindChat = open && !active && !off;
744
1083
  const phaseRef = useRef(IDLE);
745
- const latest = useRef({ live, locale, base, headers, exchange, off });
746
- latest.current = { live, locale, base, headers, exchange, off };
1084
+ const latest = useRef({
1085
+ live,
1086
+ locale,
1087
+ base,
1088
+ headers,
1089
+ off,
1090
+ currentPath,
1091
+ showing
1092
+ });
1093
+ latest.current = { live, locale, base, headers, off, currentPath, showing };
747
1094
  const conversationRef = useRef(conversation);
748
1095
  conversationRef.current = conversation;
1096
+ const waitRef = useRef(null);
1097
+ const sendingRef = useRef(false);
1098
+ const textBoxRef = useRef(false);
749
1099
  const checkRef = useRef(null);
750
1100
  const askedLocale = useRef(locale);
751
1101
  const startRef = useRef(null);
@@ -759,8 +1109,32 @@ function AvatarLiveView({
759
1109
  phaseRef.current = next;
760
1110
  setPhase(next);
761
1111
  }, []);
1112
+ const inSight = useCallback(
1113
+ () => latest.current.showing && (typeof document === "undefined" || document.visibilityState !== "hidden"),
1114
+ []
1115
+ );
1116
+ const setMode = useCallback((next) => {
1117
+ questionsRef.current = next;
1118
+ setQuestions(next);
1119
+ }, []);
1120
+ const exchangeNow = useCallback(
1121
+ () => offeredAt(
1122
+ firstExchange(conversationRef.current?.messages, {
1123
+ any: questionsRef.current === "any",
1124
+ askedHere: askedHereRef.current,
1125
+ token: conversationRef.current?.getConversationToken() ?? null,
1126
+ inFlight: inFlight(conversationRef.current)
1127
+ }),
1128
+ Date.now()
1129
+ ),
1130
+ []
1131
+ );
762
1132
  const offeredNow = useCallback(
763
- () => offeredAt(latest.current.exchange, Date.now()).answerId,
1133
+ () => exchangeNow().answerId,
1134
+ [exchangeNow]
1135
+ );
1136
+ const pathNow = useCallback(
1137
+ () => latest.current.currentPath ?? (typeof window === "undefined" ? "" : window.location.pathname),
764
1138
  []
765
1139
  );
766
1140
  const reoffer = useCallback(
@@ -793,6 +1167,17 @@ function AvatarLiveView({
793
1167
  },
794
1168
  [go]
795
1169
  );
1170
+ const goLive = useCallback(() => {
1171
+ const current = phaseRef.current;
1172
+ const handle = sessionRef.current;
1173
+ if (current.name !== "pending" || !handle) return;
1174
+ handle.speakerArrived = true;
1175
+ if (questionsRef.current === "any") {
1176
+ noteUsedToday();
1177
+ setUsedToday(true);
1178
+ }
1179
+ go({ name: "live", session: current.session });
1180
+ }, [go]);
796
1181
  const cancelStart = useCallback((reason) => {
797
1182
  const run = startRef.current;
798
1183
  if (!run) return;
@@ -800,6 +1185,36 @@ function AvatarLiveView({
800
1185
  run.cancelled = reason;
801
1186
  run.turnstile?.cancel();
802
1187
  }, []);
1188
+ const applyAvailability = useCallback(
1189
+ (result, locale2) => {
1190
+ const { live: live2 } = latest.current;
1191
+ if (result.kind === "inert") {
1192
+ setMode("suggested");
1193
+ go(INERT2);
1194
+ return;
1195
+ }
1196
+ if (result.kind === "refused") {
1197
+ setMode(result.questions);
1198
+ go({ name: "refused", reason: result.reason });
1199
+ return;
1200
+ }
1201
+ setUsedToday(usedTodayHere());
1202
+ const copy = avatarConsentCopy(result.offer.consentVersion, locale2);
1203
+ if (copy === null || copy.botCheck !== result.offer.botCheck || copy.questions !== result.offer.questions) {
1204
+ setMode("suggested");
1205
+ go(INERT2);
1206
+ return;
1207
+ }
1208
+ if (result.offer.botCheck === "turnstile" && turnstileSiteKeyOf(live2) === null) {
1209
+ setMode("suggested");
1210
+ go(INERT2);
1211
+ return;
1212
+ }
1213
+ setMode(result.offer.questions);
1214
+ go(reoffer({ ...result.offer, locale: locale2, copy }));
1215
+ },
1216
+ [go, reoffer, setMode]
1217
+ );
803
1218
  const check = useCallback(() => {
804
1219
  const { live: live2, locale: locale2, base: base2, headers: headers2, off: off2 } = latest.current;
805
1220
  checkRef.current?.abort();
@@ -821,26 +1236,9 @@ function AvatarLiveView({
821
1236
  }).then((result) => {
822
1237
  if (checkRef.current !== controller) return;
823
1238
  checkRef.current = null;
824
- if (result.kind === "inert") {
825
- go(INERT2);
826
- return;
827
- }
828
- if (result.kind === "refused") {
829
- go({ name: "refused", reason: result.reason });
830
- return;
831
- }
832
- const copy = avatarConsentCopy(result.offer.consentVersion, locale2);
833
- if (copy === null || copy.botCheck !== result.offer.botCheck) {
834
- go(INERT2);
835
- return;
836
- }
837
- if (result.offer.botCheck === "turnstile" && turnstileSiteKeyOf(live2) === null) {
838
- go(INERT2);
839
- return;
840
- }
841
- go(reoffer({ ...result.offer, locale: locale2, copy }));
1239
+ applyAvailability(result, locale2);
842
1240
  });
843
- }, [go, reoffer]);
1241
+ }, [applyAvailability, go]);
844
1242
  const begin = useCallback(
845
1243
  (session, module, offer, target) => {
846
1244
  const received = Date.now();
@@ -885,8 +1283,8 @@ function AvatarLiveView({
885
1283
  reason: run.cancelled
886
1284
  });
887
1285
  }
888
- const shown = phaseRef.current;
889
- if (claimed && shown.name === "consent" && shown.messageId === context.messageId) {
1286
+ const shown2 = phaseRef.current;
1287
+ if (claimed && shown2.name === "consent" && shown2.messageId === context.messageId) {
890
1288
  go({ name: "refused", reason: "answer_claimed" });
891
1289
  }
892
1290
  return;
@@ -921,86 +1319,186 @@ function AvatarLiveView({
921
1319
  },
922
1320
  [begin, check, go]
923
1321
  );
1322
+ const launch = useCallback(
1323
+ (offer, messageId, preloaded) => {
1324
+ const { live: live2, base: base2, headers: headers2 } = latest.current;
1325
+ if (base2 === null) return;
1326
+ const exchangeAtStart = exchangeNow();
1327
+ if (exchangeAtStart.answerId !== messageId) {
1328
+ go(reoffer(offer));
1329
+ return;
1330
+ }
1331
+ if (claimedRef.current.has(messageId)) {
1332
+ go({ name: "refused", reason: "answer_claimed" });
1333
+ return;
1334
+ }
1335
+ const refusedFrom = exchangeAtStart.offerUntil - ANSWER_OFFER_MS + SERVICE_MAX_AGE_MS;
1336
+ const siteKey = offer.botCheck === "turnstile" ? turnstileSiteKeyOf(live2) : null;
1337
+ if (offer.botCheck === "turnstile" && (siteKey === null || !canBindBotCheck())) {
1338
+ go(INERT2);
1339
+ return;
1340
+ }
1341
+ const conversationToken = conversationRef.current?.getConversationToken() ?? null;
1342
+ const idempotencyKey = newIdempotencyKey();
1343
+ if (conversationToken === null || !isAvatarConversationToken(conversationToken) || idempotencyKey === null) {
1344
+ go({ name: "ended", how: "failed" });
1345
+ return;
1346
+ }
1347
+ const body2 = {
1348
+ site: live2.site,
1349
+ locale: offer.locale,
1350
+ conversationToken,
1351
+ messageId,
1352
+ consentVersion: offer.consentVersion,
1353
+ manifestVersion: live2.manifestVersion
1354
+ };
1355
+ const run = { cancelled: null, turnstile: null };
1356
+ startRef.current = run;
1357
+ const module = preloaded ?? loadModule(live2.load);
1358
+ let turnstile = null;
1359
+ if (siteKey === null) {
1360
+ go({ name: "creating" });
1361
+ } else {
1362
+ go({ name: "bot-check", offer, messageId });
1363
+ turnstile = runTurnstile(
1364
+ botBoxRef.current,
1365
+ siteKey,
1366
+ "avatar_start",
1367
+ avatarBotBinding(body2)
1368
+ );
1369
+ run.turnstile = turnstile;
1370
+ }
1371
+ void (async () => {
1372
+ let loaded;
1373
+ let botToken;
1374
+ try {
1375
+ [loaded, botToken] = await Promise.all([module, turnstile?.token]);
1376
+ } catch {
1377
+ turnstile?.cancel();
1378
+ if (run.cancelled === null) {
1379
+ startRef.current = null;
1380
+ go(INERT2);
1381
+ }
1382
+ return;
1383
+ }
1384
+ turnstile?.cancel();
1385
+ if (run.cancelled !== null) return;
1386
+ if (!(Date.now() < refusedFrom)) {
1387
+ startRef.current = null;
1388
+ go({ name: "needs-answer", offer });
1389
+ return;
1390
+ }
1391
+ if (turnstile !== null) go({ name: "creating" });
1392
+ const result = await requestCreate({
1393
+ base: base2,
1394
+ headers: headers2,
1395
+ idempotencyKey,
1396
+ botToken,
1397
+ body: body2
1398
+ });
1399
+ if (startRef.current === run) startRef.current = null;
1400
+ settle(result, run, {
1401
+ module: loaded,
1402
+ offer,
1403
+ messageId,
1404
+ base: base2,
1405
+ headers: headers2
1406
+ });
1407
+ })();
1408
+ },
1409
+ [exchangeNow, go, reoffer, settle]
1410
+ );
924
1411
  const start = useCallback(() => {
925
1412
  const current = phaseRef.current;
926
1413
  if (current.name !== "consent") return;
927
- const { live: live2, base: base2, headers: headers2 } = latest.current;
928
- if (base2 === null) return;
929
- const { offer, messageId } = current;
930
- if (offeredNow() !== messageId) {
931
- go(reoffer(offer));
932
- return;
933
- }
934
- const refusedFrom = latest.current.exchange.offerUntil - ANSWER_OFFER_MS + SERVICE_MAX_AGE_MS;
935
- const siteKey = offer.botCheck === "turnstile" ? turnstileSiteKeyOf(live2) : null;
936
- if (offer.botCheck === "turnstile" && (siteKey === null || !canBindBotCheck())) {
937
- go(INERT2);
938
- return;
939
- }
940
- const conversationToken = conversationRef.current?.getConversationToken() ?? null;
941
- const idempotencyKey = newIdempotencyKey();
942
- if (conversationToken === null || !isAvatarConversationToken(conversationToken) || idempotencyKey === null) {
943
- go({ name: "ended", how: "failed" });
1414
+ if (current.offer.questions === "any") unlockSound(audioRef.current);
1415
+ launch(current.offer, current.messageId, null);
1416
+ }, [launch]);
1417
+ const cancelWaiting = useCallback(
1418
+ (told) => {
1419
+ const wait = waitRef.current;
1420
+ waitRef.current = null;
1421
+ wait?.check?.abort();
1422
+ const current = phaseRef.current;
1423
+ if (current.name !== "waiting") return;
1424
+ if (told) setNote("cancelled");
1425
+ go({ name: "needs-answer", offer: current.offer });
1426
+ },
1427
+ [go]
1428
+ );
1429
+ const bindingHolds = useCallback(
1430
+ (bound, exchange2) => {
1431
+ const { live: live2, locale: locale2 } = latest.current;
1432
+ return conversationRef.current?.getConversationToken() === bound.token && exchange2.question?.id === bound.questionId && live2.site === bound.site && locale2 === bound.locale && pathNow() === bound.path;
1433
+ },
1434
+ [pathNow]
1435
+ );
1436
+ const startWhenReady = useCallback(() => {
1437
+ const current = phaseRef.current;
1438
+ if (current.name !== "needs-answer") return;
1439
+ const { live: live2, locale: locale2 } = latest.current;
1440
+ const exchange2 = exchangeNow();
1441
+ const token2 = conversationRef.current?.getConversationToken() ?? null;
1442
+ if (exchange2.kind !== "asking" || !exchange2.askedHere || exchange2.question === null || exchange2.pendingAnswerId === null || token2 === null || !isAvatarConversationToken(token2)) {
944
1443
  return;
945
1444
  }
946
- const body2 = {
947
- site: live2.site,
948
- locale: offer.locale,
949
- conversationToken,
950
- messageId,
951
- consentVersion: offer.consentVersion,
952
- manifestVersion: live2.manifestVersion
953
- };
954
- const run = { cancelled: null, turnstile: null };
955
- startRef.current = run;
1445
+ unlockSound(audioRef.current);
956
1446
  const module = loadModule(live2.load);
957
- let turnstile = null;
958
- if (siteKey === null) {
959
- go({ name: "creating" });
960
- } else {
961
- go({ name: "bot-check", offer, messageId });
962
- turnstile = runTurnstile(
963
- botBoxRef.current,
964
- siteKey,
965
- "avatar_start",
966
- avatarBotBinding(body2)
967
- );
968
- run.turnstile = turnstile;
969
- }
970
- void (async () => {
971
- let loaded;
972
- let botToken;
973
- try {
974
- [loaded, botToken] = await Promise.all([module, turnstile?.token]);
975
- } catch {
976
- turnstile?.cancel();
977
- if (run.cancelled === null) {
978
- startRef.current = null;
979
- go(INERT2);
980
- }
981
- return;
982
- }
983
- turnstile?.cancel();
984
- if (run.cancelled !== null) return;
985
- if (!(Date.now() < refusedFrom)) {
986
- startRef.current = null;
987
- go({ name: "needs-answer", offer });
1447
+ module.catch(() => void 0);
1448
+ waitRef.current = {
1449
+ bound: {
1450
+ token: token2,
1451
+ questionId: exchange2.question.id,
1452
+ answerId: exchange2.pendingAnswerId,
1453
+ site: live2.site,
1454
+ locale: locale2,
1455
+ path: pathNow(),
1456
+ consentVersion: current.offer.consentVersion,
1457
+ botCheck: current.offer.botCheck
1458
+ },
1459
+ module,
1460
+ check: null
1461
+ };
1462
+ setNote(null);
1463
+ go({ name: "waiting", offer: current.offer });
1464
+ }, [exchangeNow, go, pathNow]);
1465
+ const confirm = useCallback(
1466
+ (offer, wait) => {
1467
+ const { live: live2, locale: locale2, base: base2, headers: headers2 } = latest.current;
1468
+ if (base2 === null) {
1469
+ cancelWaiting(true);
988
1470
  return;
989
1471
  }
990
- if (turnstile !== null) go({ name: "creating" });
991
- const result = await requestCreate({
1472
+ const controller = new AbortController();
1473
+ wait.check = controller;
1474
+ void requestAvailability({
992
1475
  base: base2,
993
1476
  headers: headers2,
994
- idempotencyKey,
995
- botToken,
996
- body: body2
1477
+ site: live2.site,
1478
+ locale: locale2,
1479
+ signal: controller.signal
1480
+ }).then((result) => {
1481
+ if (waitRef.current !== wait || phaseRef.current.name !== "waiting") {
1482
+ return;
1483
+ }
1484
+ waitRef.current = null;
1485
+ const exchange2 = exchangeNow();
1486
+ const same = result.kind === "available" && result.offer.consentVersion === wait.bound.consentVersion && result.offer.botCheck === wait.bound.botCheck && result.offer.questions === "any" && exchange2.kind === "answer" && exchange2.answerId === wait.bound.answerId && bindingHolds(wait.bound, exchange2);
1487
+ if (!same || result.kind !== "available") {
1488
+ applyAvailability(result, locale2);
1489
+ return;
1490
+ }
1491
+ launch({ ...offer, ...result.offer }, wait.bound.answerId, wait.module);
997
1492
  });
998
- if (startRef.current === run) startRef.current = null;
999
- settle(result, run, { module: loaded, offer, messageId, base: base2, headers: headers2 });
1000
- })();
1001
- }, [go, offeredNow, reoffer, settle]);
1493
+ },
1494
+ [applyAvailability, bindingHolds, cancelWaiting, exchangeNow, launch]
1495
+ );
1002
1496
  const stop = useCallback(() => {
1003
1497
  const current = phaseRef.current;
1498
+ if (current.name === "waiting") {
1499
+ cancelWaiting(false);
1500
+ return;
1501
+ }
1004
1502
  if (current.name === "bot-check") {
1005
1503
  cancelStart("user");
1006
1504
  go(reoffer(current.offer));
@@ -1014,11 +1512,13 @@ function AvatarLiveView({
1014
1512
  if (current.name === "pending" || current.name === "live") {
1015
1513
  finish("stopped", "user");
1016
1514
  }
1017
- }, [cancelStart, finish, go, reoffer]);
1515
+ }, [cancelStart, cancelWaiting, finish, go, reoffer]);
1018
1516
  const leave = useCallback(
1019
- (reason) => {
1517
+ (event) => {
1020
1518
  const current = phaseRef.current;
1519
+ const reason = event === "user" || event === "chat" ? "user" : "page_hide";
1021
1520
  const how = reason === "user" ? "stopped" : "interrupted";
1521
+ const keepsSession = event === "chat" || event === "hidden";
1022
1522
  switch (current.name) {
1023
1523
  case "checking": {
1024
1524
  if (reason !== "user") return;
@@ -1028,49 +1528,63 @@ function AvatarLiveView({
1028
1528
  go(IDLE);
1029
1529
  return;
1030
1530
  }
1531
+ case "waiting":
1532
+ cancelWaiting(true);
1533
+ return;
1031
1534
  case "bot-check":
1032
1535
  cancelStart(reason);
1033
1536
  go(reason === "user" ? IDLE : reoffer(current.offer));
1034
1537
  return;
1035
1538
  case "creating":
1539
+ if (keepsSession) return;
1036
1540
  cancelStart(reason);
1037
1541
  go({ name: "ended", how });
1038
1542
  return;
1039
1543
  case "pending":
1040
1544
  case "live":
1545
+ if (keepsSession) {
1546
+ if (current.name === "pending" && sessionRef.current?.speakerArrived) {
1547
+ goLive();
1548
+ }
1549
+ return;
1550
+ }
1041
1551
  finish(how, reason);
1042
1552
  return;
1043
1553
  default:
1044
1554
  return;
1045
1555
  }
1046
1556
  },
1047
- [cancelStart, finish, go, reoffer]
1557
+ [cancelStart, cancelWaiting, finish, go, goLive, reoffer]
1048
1558
  );
1049
- const showing = active && !off;
1050
1559
  useEffect(() => {
1051
1560
  if (!showing) {
1052
- leave("user");
1561
+ leave(behindChat ? "chat" : "user");
1053
1562
  return;
1054
1563
  }
1055
1564
  const current = phaseRef.current;
1565
+ if (current.name === "creating" || current.name === "pending" || current.name === "live") {
1566
+ setNow(Date.now());
1567
+ return;
1568
+ }
1056
1569
  const offered = offeredNow();
1057
1570
  const used = offered !== null && claimedRef.current.has(offered);
1058
1571
  if (current.name === "ended" && used) return;
1059
1572
  if (current.name === "refused" && current.reason === "answer_claimed" && used) {
1060
1573
  return;
1061
1574
  }
1062
- if (current.name === "idle" || current.name === "inert" || current.name === "refused" || current.name === "needs-answer" || current.name === "consent" || current.name === "ended" || // Asking, with nothing actually asking: React's strict mode unmounts and mounts a component again with the state it
1575
+ if (current.name === "idle" || current.name === "inert" || current.name === "refused" || current.name === "needs-answer" || current.name === "consent" || current.name === "declined" || current.name === "ended" || // Asking, with nothing actually asking: React's strict mode unmounts and mounts a component again with the state it
1063
1576
  // left, and the unmount in between dropped the question. (This view mounts already showing, the first time the
1064
1577
  // Avatar view opens.)
1065
1578
  current.name === "checking" && checkRef.current === null) {
1066
1579
  check();
1067
1580
  }
1068
- }, [showing, check, leave, offeredNow]);
1581
+ }, [showing, behindChat, check, leave, offeredNow]);
1069
1582
  useEffect(() => {
1070
1583
  if (askedLocale.current === locale) return;
1071
1584
  askedLocale.current = locale;
1585
+ if (phaseRef.current.name === "waiting") cancelWaiting(true);
1072
1586
  const current = phaseRef.current.name;
1073
- if (current !== "checking" && current !== "needs-answer" && current !== "consent" && current !== "refused" && current !== "inert") {
1587
+ if (current !== "checking" && current !== "needs-answer" && current !== "consent" && current !== "declined" && current !== "refused" && current !== "inert") {
1074
1588
  return;
1075
1589
  }
1076
1590
  if (active) {
@@ -1080,7 +1594,8 @@ function AvatarLiveView({
1080
1594
  checkRef.current = null;
1081
1595
  go(IDLE);
1082
1596
  }
1083
- }, [active, check, go, locale]);
1597
+ }, [active, cancelWaiting, check, go, locale]);
1598
+ const kind = shown.kind;
1084
1599
  useEffect(() => {
1085
1600
  const current = phaseRef.current;
1086
1601
  if (current.name === "needs-answer" && answerId !== null) {
@@ -1089,8 +1604,49 @@ function AvatarLiveView({
1089
1604
  go(
1090
1605
  answerId === null ? { name: "needs-answer", offer: current.offer } : { ...current, messageId: answerId }
1091
1606
  );
1607
+ } else if (current.name === "declined" && answerId === null) {
1608
+ if (kind !== "asking") go({ name: "needs-answer", offer: current.offer });
1609
+ }
1610
+ }, [answerId, kind, go]);
1611
+ useEffect(() => {
1612
+ const current = phaseRef.current;
1613
+ const wait = waitRef.current;
1614
+ if (current.name !== "waiting" || wait === null || wait.check !== null) {
1615
+ return;
1616
+ }
1617
+ const now2 = exchangeNow();
1618
+ const holds = bindingHolds(wait.bound, now2);
1619
+ if (holds && now2.kind === "asking" && now2.pendingAnswerId === wait.bound.answerId) {
1620
+ return;
1621
+ }
1622
+ if (!holds || now2.kind !== "answer" || now2.answerId !== wait.bound.answerId) {
1623
+ cancelWaiting(now2.kind !== "failed" && now2.kind !== "text-only");
1624
+ return;
1625
+ }
1626
+ confirm(current.offer, wait);
1627
+ });
1628
+ const resent = conversation?.resent ?? null;
1629
+ useEffect(() => {
1630
+ const asked = askedHereRef.current;
1631
+ if (resent === null || asked === null || !resent.replacedMessageIds.includes(asked.messageId)) {
1632
+ return;
1092
1633
  }
1093
- }, [answerId, go]);
1634
+ setAskedHere({
1635
+ messageId: resent.sent.userMessageId,
1636
+ token: resent.sent.conversationToken
1637
+ });
1638
+ }, [resent, setAskedHere]);
1639
+ const empty = (conversation?.messages.length ?? 0) === 0;
1640
+ useEffect(() => {
1641
+ if (empty) setAskedHere(null);
1642
+ }, [empty, setAskedHere]);
1643
+ useEffect(() => {
1644
+ const asked = askedHereRef.current;
1645
+ if (asked !== null && asked.token !== token) setAskedHere(null);
1646
+ }, [token, setAskedHere]);
1647
+ useEffect(() => {
1648
+ if (showing) setUsedToday(usedTodayHere());
1649
+ }, [showing]);
1094
1650
  useEffect(() => {
1095
1651
  if (offerUntil === null) return;
1096
1652
  const timer = setTimeout(
@@ -1109,7 +1665,9 @@ function AvatarLiveView({
1109
1665
  const wasLive = phaseRef.current.name === "live";
1110
1666
  switch (event.type) {
1111
1667
  case "speaker-track":
1112
- if (event.kind === "video") handle.speakerArrived = true;
1668
+ if (event.kind !== "video") return;
1669
+ handle.speakerArrived = true;
1670
+ if (!inSight()) goLive();
1113
1671
  return;
1114
1672
  case "playback-blocked":
1115
1673
  setBlocked(event.blocked);
@@ -1126,7 +1684,7 @@ function AvatarLiveView({
1126
1684
  return;
1127
1685
  }
1128
1686
  },
1129
- [finish]
1687
+ [finish, goLive, inSight]
1130
1688
  );
1131
1689
  useEffect(() => {
1132
1690
  if (phase.name !== "pending") return;
@@ -1200,11 +1758,23 @@ function AvatarLiveView({
1200
1758
  const timer = setInterval(() => setNow(Date.now()), 1e3);
1201
1759
  return () => clearInterval(timer);
1202
1760
  }, [ticking]);
1761
+ const voiceLine = phase.name === "live" ? S.speaking : phase.name === "creating" || phase.name === "pending" ? S.connecting : null;
1762
+ const onVoiceHoldRef = useRef(onVoiceHold);
1763
+ onVoiceHoldRef.current = onVoiceHold;
1764
+ useEffect(() => {
1765
+ onVoiceHoldRef.current?.(voiceLine);
1766
+ }, [voiceLine]);
1767
+ useEffect(() => () => onVoiceHoldRef.current?.(null), []);
1203
1768
  useEffect(() => {
1204
1769
  if (typeof window === "undefined") return;
1205
1770
  const onPageHide = () => leave("page_hide");
1206
1771
  const onVisibility = () => {
1207
- if (document.visibilityState === "hidden") leave("page_hide");
1772
+ if (document.visibilityState === "hidden") {
1773
+ leave("hidden");
1774
+ return;
1775
+ }
1776
+ const current = phaseRef.current.name;
1777
+ if (current === "pending" || current === "live") setNow(Date.now());
1208
1778
  };
1209
1779
  window.addEventListener("pagehide", onPageHide);
1210
1780
  document.addEventListener("visibilitychange", onVisibility);
@@ -1224,6 +1794,9 @@ function AvatarLiveView({
1224
1794
  run.cancelled = "user";
1225
1795
  run.turnstile?.cancel();
1226
1796
  }
1797
+ const wait = waitRef.current;
1798
+ waitRef.current = null;
1799
+ wait?.check?.abort();
1227
1800
  const handle = sessionRef.current;
1228
1801
  sessionRef.current = null;
1229
1802
  if (handle && !handle.done) {
@@ -1240,19 +1813,74 @@ function AvatarLiveView({
1240
1813
  },
1241
1814
  []
1242
1815
  );
1816
+ const any = questions === "any";
1817
+ const textMode = any && (usedToday && (phase.name === "needs-answer" || phase.name === "consent" || phase.name === "declined" || phase.name === "ended") || phase.name === "refused" && phase.reason === "limits");
1818
+ textBoxRef.current = textMode;
1819
+ const askState = !any ? void 0 : textMode ? "text" : phase.name === "needs-answer" ? kind === "empty" ? "ready" : kind : phase.name;
1820
+ const notNow = useCallback(() => {
1821
+ const current = phaseRef.current;
1822
+ if ((current.name === "consent" || current.name === "needs-answer") && current.offer.questions === "any") {
1823
+ go({ name: "declined", offer: current.offer });
1824
+ return;
1825
+ }
1826
+ onContinueInChat();
1827
+ }, [go, onContinueInChat]);
1828
+ const ask = useCallback(
1829
+ (text) => {
1830
+ const conversation2 = conversationRef.current;
1831
+ const question = text.trim();
1832
+ if (conversation2 === null || question === "" || sendingRef.current) {
1833
+ return;
1834
+ }
1835
+ const spoken = !textBoxRef.current;
1836
+ if (spoken && conversation2.messages.length > 0) return;
1837
+ sendingRef.current = true;
1838
+ setAskedHere(null);
1839
+ const seq = askSeqRef.current;
1840
+ setNote(null);
1841
+ setDraft("");
1842
+ void conversation2.send(question, [], spoken ? { surface: "AVATAR" } : {}).then((added) => {
1843
+ if (added === null) {
1844
+ setDraft((current) => current === "" ? question : current);
1845
+ }
1846
+ if (!spoken || added === null || askSeqRef.current !== seq || added.conversationToken !== (conversationRef.current?.getConversationToken() ?? null)) {
1847
+ return;
1848
+ }
1849
+ setAskedHere({
1850
+ messageId: added.userMessageId,
1851
+ token: added.conversationToken
1852
+ });
1853
+ }).finally(() => {
1854
+ sendingRef.current = false;
1855
+ });
1856
+ },
1857
+ [setAskedHere]
1858
+ );
1859
+ const newChatForAvatar = useCallback(() => {
1860
+ const conversation2 = conversationRef.current;
1861
+ if (conversation2 === null) return;
1862
+ const now2 = exchangeNow();
1863
+ const failedQuestion = now2.kind === "failed" ? now2.question?.content ?? null : null;
1864
+ conversation2.reset();
1865
+ setAskedHere(null);
1866
+ setNote("new-chat");
1867
+ if (failedQuestion !== null) {
1868
+ setDraft((current2) => current2 === "" ? failedQuestion : current2);
1869
+ }
1870
+ const current = phaseRef.current;
1871
+ if (current.name === "needs-answer" || current.name === "consent" || current.name === "declined") {
1872
+ go({ name: "needs-answer", offer: current.offer });
1873
+ } else {
1874
+ check();
1875
+ }
1876
+ }, [check, exchangeNow, go, setAskedHere]);
1243
1877
  useEffect(() => {
1244
1878
  if (!active) return;
1245
1879
  const focused = document.activeElement;
1246
1880
  if (focused && focused !== document.body && focused.isConnected) return;
1247
1881
  rootRef.current?.querySelector(PRIMARY)?.focus();
1248
- }, [active, phase.name, blocked]);
1249
- const onPlaying = useCallback(() => {
1250
- const current = phaseRef.current;
1251
- const handle = sessionRef.current;
1252
- if (current.name !== "pending" || !handle) return;
1253
- handle.speakerArrived = true;
1254
- go({ name: "live", session: current.session });
1255
- }, [go]);
1882
+ }, [active, phase.name, askState, shown.pendingAnswerId, blocked]);
1883
+ const onPlaying = goLive;
1256
1884
  const enablePlayback = useCallback(() => {
1257
1885
  void sessionRef.current?.joined?.enablePlayback().catch(() => void 0);
1258
1886
  }, []);
@@ -1283,7 +1911,7 @@ function AvatarLiveView({
1283
1911
  height: small ? 64 : 128,
1284
1912
  className: cn(
1285
1913
  "shrink-0 rounded-full border border-border object-cover shadow-sm",
1286
- small ? "h-16 w-16" : "h-32 w-32"
1914
+ small ? "h-16 w-16" : any ? "h-[min(12rem,32dvh)] w-[min(12rem,32dvh)]" : "h-32 w-32"
1287
1915
  )
1288
1916
  }
1289
1917
  );
@@ -1332,12 +1960,440 @@ function AvatarLiveView({
1332
1960
  children: S.stop
1333
1961
  }
1334
1962
  );
1963
+ const readInChatButton = (primary) => /* @__PURE__ */ jsx(
1964
+ "button",
1965
+ {
1966
+ type: "button",
1967
+ "data-avatar-primary": primary ? "" : void 0,
1968
+ "data-boff-explore": "avatar-live-read-in-chat",
1969
+ className: primary ? primaryButton : secondaryButton,
1970
+ onClick: onContinueInChat,
1971
+ children: S.readInChat
1972
+ }
1973
+ );
1974
+ const consentCard = (copy) => /* @__PURE__ */ jsxs(
1975
+ "section",
1976
+ {
1977
+ "aria-labelledby": consentTitleId,
1978
+ "data-boff-explore": "avatar-live-consent",
1979
+ className: "w-full max-w-sm rounded-lg border border-border p-4 text-start",
1980
+ children: [
1981
+ /* @__PURE__ */ jsx("h2", { id: consentTitleId, className: "text-base font-semibold", children: copy.title }),
1982
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm", children: productName === null || copy.introProduct === null ? copy.intro : fill(copy.introProduct, { product: productName }) }),
1983
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: copy.processing }),
1984
+ /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: copy.storage }),
1985
+ /* @__PURE__ */ jsxs(
1986
+ "a",
1987
+ {
1988
+ href: PRIVACY_HREF,
1989
+ target: "_blank",
1990
+ rel: "noopener noreferrer",
1991
+ "data-boff-explore": "avatar-live-privacy",
1992
+ className: "mt-2 inline-block text-sm font-medium text-primary underline underline-offset-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1993
+ children: [
1994
+ copy.privacyLink,
1995
+ /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
1996
+ " ",
1997
+ S.opensInNewTab
1998
+ ] })
1999
+ ]
2000
+ }
2001
+ )
2002
+ ]
2003
+ }
2004
+ );
2005
+ const consentActions = (whenReady) => /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2", children: [
2006
+ /* @__PURE__ */ jsx(
2007
+ "button",
2008
+ {
2009
+ type: "button",
2010
+ "data-avatar-primary": "",
2011
+ "data-boff-explore": whenReady ? "avatar-live-start-when-ready" : "avatar-live-start",
2012
+ className: primaryButton,
2013
+ onClick: whenReady ? startWhenReady : start,
2014
+ children: whenReady ? S.startWhenReady : S.start
2015
+ }
2016
+ ),
2017
+ /* @__PURE__ */ jsx(
2018
+ "button",
2019
+ {
2020
+ type: "button",
2021
+ "data-boff-explore": "avatar-live-not-now",
2022
+ className: secondaryButton,
2023
+ onClick: notNow,
2024
+ children: S.notNow
2025
+ }
2026
+ )
2027
+ ] });
2028
+ const liveMedia = (playing) => /* @__PURE__ */ jsxs(
2029
+ "div",
2030
+ {
2031
+ className: cn(
2032
+ "relative shrink-0 overflow-hidden rounded-xl border border-border bg-muted",
2033
+ any ? "h-[min(12rem,32dvh)] w-[min(12rem,32dvh)]" : "h-48 w-48"
2034
+ ),
2035
+ children: [
2036
+ /* @__PURE__ */ jsx(
2037
+ "video",
2038
+ {
2039
+ ref: videoRef,
2040
+ "aria-label": S.video,
2041
+ "data-boff-explore": "avatar-live-video",
2042
+ playsInline: true,
2043
+ autoPlay: true,
2044
+ muted: true,
2045
+ onPlaying,
2046
+ className: cn("h-full w-full object-cover", !playing && "opacity-0")
2047
+ }
2048
+ ),
2049
+ playing ? null : /* @__PURE__ */ jsx(
2050
+ "img",
2051
+ {
2052
+ src: still.src,
2053
+ alt: "",
2054
+ decoding: "async",
2055
+ className: "absolute inset-0 h-full w-full object-cover"
2056
+ }
2057
+ ),
2058
+ any ? null : /* @__PURE__ */ jsx("audio", { ref: audioRef, "data-boff-explore": "avatar-live-audio" })
2059
+ ]
2060
+ }
2061
+ );
2062
+ const liveBody = (session) => /* @__PURE__ */ jsxs(Fragment, { children: [
2063
+ /* @__PURE__ */ jsx(
2064
+ "section",
2065
+ {
2066
+ "aria-label": S.captions,
2067
+ "data-boff-explore": "avatar-live-captions",
2068
+ lang: session.captions.locale || void 0,
2069
+ dir: "auto",
2070
+ tabIndex: 0,
2071
+ className: "max-h-32 w-full max-w-sm overflow-y-auto rounded-lg bg-muted p-3 text-start text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
2072
+ children: session.captions.text
2073
+ }
2074
+ ),
2075
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: S.answerInChat }),
2076
+ /* @__PURE__ */ jsxs("p", { className: "text-sm tabular-nums text-muted-foreground", children: [
2077
+ S.timeLeft,
2078
+ " ",
2079
+ /* @__PURE__ */ jsx("span", { role: "timer", "data-boff-explore": "avatar-live-countdown", children: clock(Math.max(0, (session.endsAt - now) / 1e3)) })
2080
+ ] })
2081
+ ] });
2082
+ const liveActions = (back) => /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2", children: [
2083
+ blocked ? /* @__PURE__ */ jsx(
2084
+ "button",
2085
+ {
2086
+ type: "button",
2087
+ "data-boff-explore": "avatar-live-sound",
2088
+ className: primaryButton,
2089
+ onClick: enablePlayback,
2090
+ children: S.tapForSound
2091
+ }
2092
+ ) : null,
2093
+ stopButton,
2094
+ back
2095
+ ] });
2096
+ const badge = /* @__PURE__ */ jsx(
2097
+ "p",
2098
+ {
2099
+ "data-boff-explore": "avatar-live-badge",
2100
+ className: "rounded-full bg-muted px-3 py-1 text-xs font-medium text-foreground",
2101
+ children: S.badge
2102
+ }
2103
+ );
2104
+ if (any) {
2105
+ let status = "";
2106
+ let outcome2 = false;
2107
+ let lead = null;
2108
+ let top2 = null;
2109
+ let media2 = stillPicture(false);
2110
+ let body2 = null;
2111
+ let actions2 = null;
2112
+ let newChatOffer = false;
2113
+ let box = null;
2114
+ const first = exchange.question;
2115
+ const questionLine = first === null ? null : /* @__PURE__ */ jsx(
2116
+ "p",
2117
+ {
2118
+ "data-boff-explore": "avatar-live-question",
2119
+ dir: "auto",
2120
+ className: "line-clamp-2 w-full max-w-sm break-words text-sm font-medium",
2121
+ children: first.content
2122
+ }
2123
+ );
2124
+ const activity = conversation?.activity ?? null;
2125
+ const activityLine = activity === null ? null : /* @__PURE__ */ jsx(
2126
+ "p",
2127
+ {
2128
+ "data-boff-explore": "avatar-live-activity",
2129
+ className: "text-xs text-muted-foreground",
2130
+ children: activity
2131
+ }
2132
+ );
2133
+ if (textMode) {
2134
+ const messages = conversation?.messages ?? [];
2135
+ let questionAt = messages.length - 1;
2136
+ while (questionAt >= 0 && messages[questionAt]?.role !== "USER") {
2137
+ questionAt -= 1;
2138
+ }
2139
+ const lastQuestion = questionAt === -1 ? void 0 : messages[questionAt];
2140
+ const after = questionAt === -1 ? void 0 : messages[questionAt + 1];
2141
+ const lastAnswer = after?.role === "ASSISTANT" ? after : void 0;
2142
+ const chatPhase = conversation?.phase;
2143
+ const unanswered = lastQuestion === void 0 || lastAnswer !== void 0 ? null : chatPhase === "sending" || chatPhase === "streaming" ? "waiting" : chatPhase === "error" || chatPhase === "limited" ? "failed" : null;
2144
+ if (phase.name === "refused") {
2145
+ status = S[REFUSAL_TEXT[phase.reason]];
2146
+ outcome2 = true;
2147
+ } else if (phase.name === "ended") {
2148
+ status = S[phase.how];
2149
+ outcome2 = true;
2150
+ }
2151
+ body2 = /* @__PURE__ */ jsxs(Fragment, { children: [
2152
+ lastQuestion === void 0 ? null : /* @__PURE__ */ jsx(
2153
+ "p",
2154
+ {
2155
+ "data-boff-explore": "avatar-live-question",
2156
+ dir: "auto",
2157
+ className: "line-clamp-2 w-full max-w-sm break-words text-sm font-medium",
2158
+ children: lastQuestion.content
2159
+ }
2160
+ ),
2161
+ lastAnswer === void 0 ? unanswered === "waiting" ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: activity ?? S.gettingAnswer }) : unanswered === "failed" ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: S.answerFailed }) : null : lastAnswer.status === "PENDING" || lastAnswer.status === "RUNNING" ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: activity ?? S.gettingAnswer }) : lastAnswer.status === "FAILED" ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: S.answerFailed }) : /* @__PURE__ */ jsx(
2162
+ "p",
2163
+ {
2164
+ "data-boff-explore": "avatar-live-text-answer",
2165
+ dir: "auto",
2166
+ className: "line-clamp-6 w-full max-w-sm whitespace-pre-wrap break-words text-start text-sm",
2167
+ children: lastAnswer.content
2168
+ }
2169
+ )
2170
+ ] });
2171
+ const ended = phase.name === "ended";
2172
+ actions2 = lastAnswer === void 0 && unanswered !== "failed" && !ended ? null : readInChatButton(ended);
2173
+ box = "text";
2174
+ } else {
2175
+ switch (phase.name) {
2176
+ case "checking":
2177
+ status = S.checking;
2178
+ break;
2179
+ case "needs-answer":
2180
+ switch (shown.kind) {
2181
+ case "empty":
2182
+ status = note === "new-chat" ? S.newChatStarted : "";
2183
+ lead = /* @__PURE__ */ jsx("p", { className: "text-base font-semibold", children: S.askHeadline });
2184
+ box = "spoken";
2185
+ break;
2186
+ case "asking":
2187
+ case "answer": {
2188
+ const card = shown.askedHere && shown.pendingAnswerId !== null;
2189
+ status = note === "cancelled" ? S.armedCancelled : S.gettingAnswer;
2190
+ if (card) media2 = stillPicture(true);
2191
+ body2 = /* @__PURE__ */ jsxs(Fragment, { children: [
2192
+ questionLine,
2193
+ activityLine,
2194
+ card ? consentCard(phase.offer.copy) : null
2195
+ ] });
2196
+ actions2 = card ? consentActions(true) : null;
2197
+ break;
2198
+ }
2199
+ case "text-only":
2200
+ status = S.notSpoken;
2201
+ outcome2 = true;
2202
+ body2 = questionLine;
2203
+ actions2 = readInChatButton(false);
2204
+ newChatOffer = true;
2205
+ break;
2206
+ case "failed":
2207
+ status = S.answerFailed;
2208
+ outcome2 = true;
2209
+ body2 = questionLine;
2210
+ newChatOffer = true;
2211
+ break;
2212
+ case "ask-here":
2213
+ status = S.askHere;
2214
+ outcome2 = true;
2215
+ actions2 = readInChatButton(false);
2216
+ newChatOffer = true;
2217
+ break;
2218
+ default:
2219
+ status = S.startNewChat;
2220
+ outcome2 = true;
2221
+ body2 = /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: S.startNewChatDetailAny });
2222
+ actions2 = readInChatButton(false);
2223
+ newChatOffer = true;
2224
+ break;
2225
+ }
2226
+ break;
2227
+ case "consent":
2228
+ media2 = stillPicture(true);
2229
+ body2 = /* @__PURE__ */ jsxs(Fragment, { children: [
2230
+ questionLine,
2231
+ consentCard(phase.offer.copy)
2232
+ ] });
2233
+ actions2 = consentActions(false);
2234
+ break;
2235
+ case "declined":
2236
+ status = S.declined;
2237
+ actions2 = readInChatButton(true);
2238
+ break;
2239
+ case "waiting":
2240
+ status = S.armed;
2241
+ body2 = questionLine;
2242
+ actions2 = /* @__PURE__ */ jsx(
2243
+ "button",
2244
+ {
2245
+ type: "button",
2246
+ "data-avatar-primary": "",
2247
+ "data-boff-explore": "avatar-live-cancel",
2248
+ className: secondaryButton,
2249
+ onClick: stop,
2250
+ children: S.cancel
2251
+ }
2252
+ );
2253
+ break;
2254
+ case "refused":
2255
+ outcome2 = true;
2256
+ if (phase.reason === "not_eligible" || phase.reason === "answer_claimed") {
2257
+ status = phase.reason === "not_eligible" ? S.notEligibleAny : S.answerClaimed;
2258
+ actions2 = readInChatButton(false);
2259
+ newChatOffer = true;
2260
+ } else {
2261
+ status = S[REFUSAL_TEXT[phase.reason]];
2262
+ actions2 = continueButton;
2263
+ }
2264
+ break;
2265
+ case "bot-check":
2266
+ status = S.botCheck;
2267
+ actions2 = stopButton;
2268
+ break;
2269
+ case "creating":
2270
+ status = S.connecting;
2271
+ actions2 = stopButton;
2272
+ break;
2273
+ case "pending":
2274
+ case "live":
2275
+ status = phase.name === "live" ? S.speaking : S.connecting;
2276
+ top2 = badge;
2277
+ media2 = liveMedia(phase.name === "live");
2278
+ body2 = liveBody(phase.session);
2279
+ actions2 = liveActions(readInChatButton(false));
2280
+ break;
2281
+ case "ended":
2282
+ status = S[phase.how];
2283
+ outcome2 = true;
2284
+ actions2 = readInChatButton(true);
2285
+ newChatOffer = true;
2286
+ break;
2287
+ }
2288
+ }
2289
+ const reason = inFlight(conversation) ? S.askBusy : conversation?.canSend ? null : S.askUnavailable;
2290
+ const chips = box !== null && shown.kind === "empty" && prompts.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
2291
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: tryAsking }),
2292
+ /* @__PURE__ */ jsx("div", { className: "flex max-h-28 flex-wrap gap-2 overflow-y-auto", children: prompts.map((prompt) => /* @__PURE__ */ jsx(
2293
+ "button",
2294
+ {
2295
+ type: "button",
2296
+ "data-boff-explore": "avatar-live-chip",
2297
+ "aria-disabled": reason !== null ? "true" : void 0,
2298
+ className: "min-h-11 rounded-full border border-border bg-background px-3 py-2 text-start text-sm text-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring aria-disabled:cursor-not-allowed aria-disabled:opacity-50 motion-reduce:transition-none",
2299
+ onClick: () => {
2300
+ if (reason === null) ask(prompt);
2301
+ },
2302
+ children: prompt
2303
+ },
2304
+ prompt
2305
+ )) })
2306
+ ] }) : null;
2307
+ const foot = box === null && !newChatOffer ? null : /* @__PURE__ */ jsxs(
2308
+ "div",
2309
+ {
2310
+ "data-boff-explore": "avatar-live-foot",
2311
+ className: "flex shrink-0 flex-col gap-2 border-t border-border bg-background p-3 pb-[max(0.75rem,env(safe-area-inset-bottom))]",
2312
+ children: [
2313
+ newChatOffer ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1 text-center", children: [
2314
+ /* @__PURE__ */ jsx(
2315
+ "button",
2316
+ {
2317
+ type: "button",
2318
+ "data-avatar-primary": "",
2319
+ "data-boff-explore": "avatar-live-new-chat-avatar",
2320
+ className: cn(primaryButton, "min-h-11"),
2321
+ onClick: newChatForAvatar,
2322
+ children: S.newChatForAvatar
2323
+ }
2324
+ ),
2325
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: S.newChatNote })
2326
+ ] }) : null,
2327
+ chips,
2328
+ box !== null && usedToday ? /* @__PURE__ */ jsx(
2329
+ "p",
2330
+ {
2331
+ "data-boff-explore": "avatar-live-used-today",
2332
+ className: "text-xs text-muted-foreground",
2333
+ children: S.usedToday
2334
+ }
2335
+ ) : null,
2336
+ box === null ? null : /* @__PURE__ */ jsx(
2337
+ AskBox,
2338
+ {
2339
+ value: draft,
2340
+ maxLength: askMaxLength,
2341
+ onChange: setDraft,
2342
+ onAsk: ask,
2343
+ reason,
2344
+ label: S.askLabel,
2345
+ placeholder: S.askPlaceholder,
2346
+ send,
2347
+ footer: S.askFooter,
2348
+ noFiles: S.askNoFiles,
2349
+ hint: box === "spoken" ? S.needsAnswerAny : null,
2350
+ touchFirst
2351
+ }
2352
+ )
2353
+ ]
2354
+ }
2355
+ );
2356
+ return /* @__PURE__ */ jsxs(
2357
+ "div",
2358
+ {
2359
+ ref: rootRef,
2360
+ "data-boff-explore": "avatar-live",
2361
+ "data-phase": phase.name,
2362
+ "data-ask": askState,
2363
+ className: "absolute inset-0 flex flex-col bg-background",
2364
+ children: [
2365
+ /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col items-center gap-4 overflow-y-auto p-6 text-center", children: [
2366
+ top2,
2367
+ media2,
2368
+ lead,
2369
+ /* @__PURE__ */ jsx(
2370
+ "p",
2371
+ {
2372
+ role: "status",
2373
+ "data-boff-explore": "avatar-live-status",
2374
+ className: outcome2 ? "text-base font-semibold" : "text-sm text-muted-foreground",
2375
+ children: status
2376
+ }
2377
+ ),
2378
+ body2,
2379
+ /* @__PURE__ */ jsx("div", { ref: botBoxRef, "data-boff-explore": "avatar-live-bot-check" }),
2380
+ actions2
2381
+ ] }),
2382
+ foot,
2383
+ /* @__PURE__ */ jsx("audio", { ref: audioRef, "data-boff-explore": "avatar-live-audio" })
2384
+ ]
2385
+ }
2386
+ );
2387
+ }
1335
2388
  const headline = {
1336
2389
  idle: "",
1337
2390
  checking: S.checking,
1338
2391
  inert: "",
1339
- "needs-answer": startOver ? S.startNewChat : S.needsAnswer,
2392
+ // A question asked and its answer on its way: the answer is coming (the avatar is offered once it completes).
2393
+ "needs-answer": startOver ? S.startNewChat : shown.kind === "asking" ? S.gettingAnswer : S.needsAnswer,
1340
2394
  consent: "",
2395
+ declined: S.declined,
2396
+ waiting: S.armed,
1341
2397
  refused: phase.name === "refused" ? S[REFUSAL_TEXT[phase.reason]] : "",
1342
2398
  "bot-check": S.botCheck,
1343
2399
  creating: S.connecting,
@@ -1355,65 +2411,11 @@ function AvatarLiveView({
1355
2411
  body = /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: startOver ? S.startNewChatDetail : S.needsAnswerDetail });
1356
2412
  actions = startOver ? startOverActions : continueButton;
1357
2413
  break;
1358
- case "consent": {
1359
- const { copy } = phase.offer;
2414
+ case "consent":
1360
2415
  media = stillPicture(true);
1361
- body = /* @__PURE__ */ jsxs(
1362
- "section",
1363
- {
1364
- "aria-labelledby": consentTitleId,
1365
- "data-boff-explore": "avatar-live-consent",
1366
- className: "w-full max-w-sm rounded-lg border border-border p-4 text-start",
1367
- children: [
1368
- /* @__PURE__ */ jsx("h2", { id: consentTitleId, className: "text-base font-semibold", children: copy.title }),
1369
- /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm", children: productName === null || copy.introProduct === null ? copy.intro : fill(copy.introProduct, { product: productName }) }),
1370
- /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: copy.processing }),
1371
- /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: copy.storage }),
1372
- /* @__PURE__ */ jsxs(
1373
- "a",
1374
- {
1375
- href: PRIVACY_HREF,
1376
- target: "_blank",
1377
- rel: "noopener noreferrer",
1378
- "data-boff-explore": "avatar-live-privacy",
1379
- className: "mt-2 inline-block text-sm font-medium text-primary underline underline-offset-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1380
- children: [
1381
- copy.privacyLink,
1382
- /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
1383
- " ",
1384
- S.opensInNewTab
1385
- ] })
1386
- ]
1387
- }
1388
- )
1389
- ]
1390
- }
1391
- );
1392
- actions = /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2", children: [
1393
- /* @__PURE__ */ jsx(
1394
- "button",
1395
- {
1396
- type: "button",
1397
- "data-avatar-primary": "",
1398
- "data-boff-explore": "avatar-live-start",
1399
- className: primaryButton,
1400
- onClick: start,
1401
- children: S.start
1402
- }
1403
- ),
1404
- /* @__PURE__ */ jsx(
1405
- "button",
1406
- {
1407
- type: "button",
1408
- "data-boff-explore": "avatar-live-not-now",
1409
- className: secondaryButton,
1410
- onClick: onContinueInChat,
1411
- children: S.notNow
1412
- }
1413
- )
1414
- ] });
2416
+ body = consentCard(phase.offer.copy);
2417
+ actions = consentActions(false);
1415
2418
  break;
1416
- }
1417
2419
  case "refused":
1418
2420
  actions = phase.reason === "not_eligible" ? startOverActions : continueButton;
1419
2421
  break;
@@ -1422,77 +2424,11 @@ function AvatarLiveView({
1422
2424
  actions = stopButton;
1423
2425
  break;
1424
2426
  case "pending":
1425
- case "live": {
1426
- const playing = phase.name === "live";
1427
- const left = Math.max(0, (phase.session.endsAt - now) / 1e3);
1428
- top = /* @__PURE__ */ jsx(
1429
- "p",
1430
- {
1431
- "data-boff-explore": "avatar-live-badge",
1432
- className: "rounded-full bg-muted px-3 py-1 text-xs font-medium text-foreground",
1433
- children: S.badge
1434
- }
1435
- );
1436
- media = /* @__PURE__ */ jsxs("div", { className: "relative h-48 w-48 shrink-0 overflow-hidden rounded-xl border border-border bg-muted", children: [
1437
- /* @__PURE__ */ jsx(
1438
- "video",
1439
- {
1440
- ref: videoRef,
1441
- "aria-label": S.video,
1442
- "data-boff-explore": "avatar-live-video",
1443
- playsInline: true,
1444
- autoPlay: true,
1445
- muted: true,
1446
- onPlaying,
1447
- className: cn(
1448
- "h-full w-full object-cover",
1449
- !playing && "opacity-0"
1450
- )
1451
- }
1452
- ),
1453
- playing ? null : /* @__PURE__ */ jsx(
1454
- "img",
1455
- {
1456
- src: still.src,
1457
- alt: "",
1458
- decoding: "async",
1459
- className: "absolute inset-0 h-full w-full object-cover"
1460
- }
1461
- ),
1462
- /* @__PURE__ */ jsx("audio", { ref: audioRef, "data-boff-explore": "avatar-live-audio" })
1463
- ] });
1464
- body = /* @__PURE__ */ jsxs(Fragment, { children: [
1465
- /* @__PURE__ */ jsx(
1466
- "section",
1467
- {
1468
- "aria-label": S.captions,
1469
- "data-boff-explore": "avatar-live-captions",
1470
- lang: phase.session.captions.locale || void 0,
1471
- dir: "auto",
1472
- tabIndex: 0,
1473
- className: "max-h-32 w-full max-w-sm overflow-y-auto rounded-lg bg-muted p-3 text-start text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
1474
- children: phase.session.captions.text
1475
- }
1476
- ),
1477
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: S.answerInChat }),
1478
- /* @__PURE__ */ jsxs("p", { className: "text-sm tabular-nums text-muted-foreground", children: [
1479
- S.timeLeft,
1480
- " ",
1481
- /* @__PURE__ */ jsx("span", { role: "timer", "data-boff-explore": "avatar-live-countdown", children: clock(left) })
1482
- ] })
1483
- ] });
1484
- actions = /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2", children: [
1485
- blocked ? /* @__PURE__ */ jsx(
1486
- "button",
1487
- {
1488
- type: "button",
1489
- "data-boff-explore": "avatar-live-sound",
1490
- className: primaryButton,
1491
- onClick: enablePlayback,
1492
- children: S.tapForSound
1493
- }
1494
- ) : null,
1495
- stopButton,
2427
+ case "live":
2428
+ top = badge;
2429
+ media = liveMedia(phase.name === "live");
2430
+ body = liveBody(phase.session);
2431
+ actions = liveActions(
1496
2432
  /* @__PURE__ */ jsx(
1497
2433
  "button",
1498
2434
  {
@@ -1503,9 +2439,8 @@ function AvatarLiveView({
1503
2439
  children: continueInChat
1504
2440
  }
1505
2441
  )
1506
- ] });
2442
+ );
1507
2443
  break;
1508
- }
1509
2444
  case "ended":
1510
2445
  body = /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: S.answerInChat });
1511
2446
  actions = /* @__PURE__ */ jsx(
@@ -1547,6 +2482,119 @@ function AvatarLiveView({
1547
2482
  }
1548
2483
  );
1549
2484
  }
2485
+ function AskBox({
2486
+ value,
2487
+ maxLength,
2488
+ onChange,
2489
+ onAsk,
2490
+ reason,
2491
+ label,
2492
+ placeholder,
2493
+ send,
2494
+ footer,
2495
+ noFiles,
2496
+ hint,
2497
+ touchFirst
2498
+ }) {
2499
+ const uid = useId();
2500
+ const reasonId = `${uid}-reason`;
2501
+ const hintId = `${uid}-hint`;
2502
+ const footerId = `${uid}-footer`;
2503
+ const [refusedFiles, setRefusedFiles] = useState(false);
2504
+ const empty = value.trim() === "";
2505
+ const submit = () => {
2506
+ if (reason !== null || empty) return;
2507
+ onAsk(value);
2508
+ };
2509
+ const onKeyDown = (event) => {
2510
+ if (event.nativeEvent.isComposing || event.keyCode === 229) {
2511
+ if (event.key === "Escape") event.stopPropagation();
2512
+ return;
2513
+ }
2514
+ if (event.key === "Enter" && !event.shiftKey) {
2515
+ event.preventDefault();
2516
+ submit();
2517
+ }
2518
+ };
2519
+ const onPaste = (event) => {
2520
+ if (event.clipboardData.files.length === 0) return;
2521
+ event.preventDefault();
2522
+ setRefusedFiles(true);
2523
+ };
2524
+ const onDrop = (event) => {
2525
+ if (!Array.from(event.dataTransfer.types).includes("Files")) return;
2526
+ event.preventDefault();
2527
+ setRefusedFiles(true);
2528
+ };
2529
+ return /* @__PURE__ */ jsxs(
2530
+ "form",
2531
+ {
2532
+ "data-boff-explore": "avatar-live-ask",
2533
+ className: "flex w-full flex-col gap-1",
2534
+ onSubmit: (event) => {
2535
+ event.preventDefault();
2536
+ submit();
2537
+ },
2538
+ children: [
2539
+ /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2", children: [
2540
+ /* @__PURE__ */ jsx(
2541
+ "textarea",
2542
+ {
2543
+ value,
2544
+ rows: 1,
2545
+ maxLength,
2546
+ "aria-label": label,
2547
+ "aria-describedby": hint === null ? footerId : `${hintId} ${footerId}`,
2548
+ placeholder,
2549
+ dir: "auto",
2550
+ "data-avatar-primary": touchFirst ? void 0 : "",
2551
+ "data-boff-explore": "avatar-live-ask-input",
2552
+ className: "max-h-32 min-h-11 flex-1 resize-none rounded-md border border-border bg-background px-3 py-2.5 text-base text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring sm:text-sm",
2553
+ onChange: (event) => {
2554
+ setRefusedFiles(false);
2555
+ onChange(event.target.value);
2556
+ },
2557
+ onKeyDown,
2558
+ onPaste,
2559
+ onDrop
2560
+ }
2561
+ ),
2562
+ /* @__PURE__ */ jsx(
2563
+ "button",
2564
+ {
2565
+ type: "submit",
2566
+ "aria-disabled": reason !== null || empty ? "true" : void 0,
2567
+ "aria-describedby": reason !== null ? reasonId : void 0,
2568
+ "data-avatar-primary": touchFirst ? "" : void 0,
2569
+ "data-boff-explore": "avatar-live-ask-send",
2570
+ className: "inline-flex h-11 min-w-11 shrink-0 items-center justify-center rounded-md bg-primary px-4 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 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 motion-reduce:transition-none",
2571
+ children: send
2572
+ }
2573
+ )
2574
+ ] }),
2575
+ reason === null ? null : /* @__PURE__ */ jsx(
2576
+ "p",
2577
+ {
2578
+ id: reasonId,
2579
+ "data-boff-explore": "avatar-live-ask-reason",
2580
+ className: "text-xs text-muted-foreground",
2581
+ children: reason
2582
+ }
2583
+ ),
2584
+ refusedFiles ? /* @__PURE__ */ jsx(
2585
+ "p",
2586
+ {
2587
+ "data-boff-explore": "avatar-live-ask-files",
2588
+ className: "text-xs text-muted-foreground",
2589
+ children: noFiles
2590
+ }
2591
+ ) : null,
2592
+ hint === null ? null : /* @__PURE__ */ jsx("p", { id: hintId, className: "sr-only", children: hint }),
2593
+ /* @__PURE__ */ jsx("p", { id: footerId, className: "text-xs text-muted-foreground", children: footer })
2594
+ ]
2595
+ }
2596
+ );
2597
+ }
1550
2598
 
1551
2599
  export { AvatarLiveView };
1552
2600
  //# sourceMappingURL=index.mjs.map