@burdenoff/website-sdk 2026.1102.3 → 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.
@@ -1024,11 +1024,13 @@ function clock(seconds) {
1024
1024
  function AvatarLiveView({
1025
1025
  live,
1026
1026
  active,
1027
+ open,
1027
1028
  locale,
1028
1029
  productName,
1029
1030
  still,
1030
1031
  fallback,
1031
1032
  onContinueInChat,
1033
+ onVoiceHold,
1032
1034
  continueInChat,
1033
1035
  newChat,
1034
1036
  send,
@@ -1076,9 +1078,19 @@ function AvatarLiveView({
1076
1078
  const askMaxLength = chatMaxLength !== void 0 && Number.isFinite(chatMaxLength) && chatMaxLength >= 1 ? Math.min(ASK_MAX_LENGTH, Math.floor(chatMaxLength)) : ASK_MAX_LENGTH;
1077
1079
  const [note, setNote] = useState(null);
1078
1080
  const [usedToday, setUsedToday] = useState(false);
1081
+ const showing = active && !off;
1082
+ const behindChat = open && !active && !off;
1079
1083
  const phaseRef = useRef(IDLE);
1080
- const latest = useRef({ live, locale, base, headers, off, currentPath });
1081
- latest.current = { live, locale, base, headers, off, currentPath };
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 };
1082
1094
  const conversationRef = useRef(conversation);
1083
1095
  conversationRef.current = conversation;
1084
1096
  const waitRef = useRef(null);
@@ -1097,6 +1109,10 @@ function AvatarLiveView({
1097
1109
  phaseRef.current = next;
1098
1110
  setPhase(next);
1099
1111
  }, []);
1112
+ const inSight = useCallback(
1113
+ () => latest.current.showing && (typeof document === "undefined" || document.visibilityState !== "hidden"),
1114
+ []
1115
+ );
1100
1116
  const setMode = useCallback((next) => {
1101
1117
  questionsRef.current = next;
1102
1118
  setQuestions(next);
@@ -1151,6 +1167,17 @@ function AvatarLiveView({
1151
1167
  },
1152
1168
  [go]
1153
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]);
1154
1181
  const cancelStart = useCallback((reason) => {
1155
1182
  const run = startRef.current;
1156
1183
  if (!run) return;
@@ -1487,9 +1514,11 @@ function AvatarLiveView({
1487
1514
  }
1488
1515
  }, [cancelStart, cancelWaiting, finish, go, reoffer]);
1489
1516
  const leave = useCallback(
1490
- (reason) => {
1517
+ (event) => {
1491
1518
  const current = phaseRef.current;
1519
+ const reason = event === "user" || event === "chat" ? "user" : "page_hide";
1492
1520
  const how = reason === "user" ? "stopped" : "interrupted";
1521
+ const keepsSession = event === "chat" || event === "hidden";
1493
1522
  switch (current.name) {
1494
1523
  case "checking": {
1495
1524
  if (reason !== "user") return;
@@ -1507,26 +1536,36 @@ function AvatarLiveView({
1507
1536
  go(reason === "user" ? IDLE : reoffer(current.offer));
1508
1537
  return;
1509
1538
  case "creating":
1539
+ if (keepsSession) return;
1510
1540
  cancelStart(reason);
1511
1541
  go({ name: "ended", how });
1512
1542
  return;
1513
1543
  case "pending":
1514
1544
  case "live":
1545
+ if (keepsSession) {
1546
+ if (current.name === "pending" && sessionRef.current?.speakerArrived) {
1547
+ goLive();
1548
+ }
1549
+ return;
1550
+ }
1515
1551
  finish(how, reason);
1516
1552
  return;
1517
1553
  default:
1518
1554
  return;
1519
1555
  }
1520
1556
  },
1521
- [cancelStart, cancelWaiting, finish, go, reoffer]
1557
+ [cancelStart, cancelWaiting, finish, go, goLive, reoffer]
1522
1558
  );
1523
- const showing = active && !off;
1524
1559
  useEffect(() => {
1525
1560
  if (!showing) {
1526
- leave("user");
1561
+ leave(behindChat ? "chat" : "user");
1527
1562
  return;
1528
1563
  }
1529
1564
  const current = phaseRef.current;
1565
+ if (current.name === "creating" || current.name === "pending" || current.name === "live") {
1566
+ setNow(Date.now());
1567
+ return;
1568
+ }
1530
1569
  const offered = offeredNow();
1531
1570
  const used = offered !== null && claimedRef.current.has(offered);
1532
1571
  if (current.name === "ended" && used) return;
@@ -1539,7 +1578,7 @@ function AvatarLiveView({
1539
1578
  current.name === "checking" && checkRef.current === null) {
1540
1579
  check();
1541
1580
  }
1542
- }, [showing, check, leave, offeredNow]);
1581
+ }, [showing, behindChat, check, leave, offeredNow]);
1543
1582
  useEffect(() => {
1544
1583
  if (askedLocale.current === locale) return;
1545
1584
  askedLocale.current = locale;
@@ -1626,7 +1665,9 @@ function AvatarLiveView({
1626
1665
  const wasLive = phaseRef.current.name === "live";
1627
1666
  switch (event.type) {
1628
1667
  case "speaker-track":
1629
- if (event.kind === "video") handle.speakerArrived = true;
1668
+ if (event.kind !== "video") return;
1669
+ handle.speakerArrived = true;
1670
+ if (!inSight()) goLive();
1630
1671
  return;
1631
1672
  case "playback-blocked":
1632
1673
  setBlocked(event.blocked);
@@ -1643,7 +1684,7 @@ function AvatarLiveView({
1643
1684
  return;
1644
1685
  }
1645
1686
  },
1646
- [finish]
1687
+ [finish, goLive, inSight]
1647
1688
  );
1648
1689
  useEffect(() => {
1649
1690
  if (phase.name !== "pending") return;
@@ -1717,11 +1758,23 @@ function AvatarLiveView({
1717
1758
  const timer = setInterval(() => setNow(Date.now()), 1e3);
1718
1759
  return () => clearInterval(timer);
1719
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), []);
1720
1768
  useEffect(() => {
1721
1769
  if (typeof window === "undefined") return;
1722
1770
  const onPageHide = () => leave("page_hide");
1723
1771
  const onVisibility = () => {
1724
- 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());
1725
1778
  };
1726
1779
  window.addEventListener("pagehide", onPageHide);
1727
1780
  document.addEventListener("visibilitychange", onVisibility);
@@ -1827,17 +1880,7 @@ function AvatarLiveView({
1827
1880
  if (focused && focused !== document.body && focused.isConnected) return;
1828
1881
  rootRef.current?.querySelector(PRIMARY)?.focus();
1829
1882
  }, [active, phase.name, askState, shown.pendingAnswerId, blocked]);
1830
- const onPlaying = useCallback(() => {
1831
- const current = phaseRef.current;
1832
- const handle = sessionRef.current;
1833
- if (current.name !== "pending" || !handle) return;
1834
- handle.speakerArrived = true;
1835
- if (questionsRef.current === "any") {
1836
- noteUsedToday();
1837
- setUsedToday(true);
1838
- }
1839
- go({ name: "live", session: current.session });
1840
- }, [go]);
1883
+ const onPlaying = goLive;
1841
1884
  const enablePlayback = useCallback(() => {
1842
1885
  void sessionRef.current?.joined?.enablePlayback().catch(() => void 0);
1843
1886
  }, []);