@ai-matrx/meet 0.11.50 → 0.11.52

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.
@@ -1285,6 +1285,22 @@ async function setGuestRecordAccess(args) {
1285
1285
  };
1286
1286
  }
1287
1287
 
1288
+ // src/core/captions-state.ts
1289
+ var CAPTIONS_TIMEOUT_REASON = "Captions are unavailable right now.";
1290
+ function resolveCaptionsState(i) {
1291
+ if (!i.on) return { state: "off", reason: null, remedy: null };
1292
+ if (i.takerState === "transcribing" && i.takerPresent) return { state: "live", reason: null, remedy: null };
1293
+ if (i.takerState === "joining" && !i.timedOut) return { state: "starting", reason: null, remedy: null };
1294
+ if (i.refusal !== null && i.refusal.reason.length > 0) {
1295
+ return { state: "unavailable", reason: i.refusal.reason, remedy: i.refusal.remedy.length > 0 ? i.refusal.remedy : null };
1296
+ }
1297
+ if (i.takerState === "ended" || i.takerState === "handoff_pending") {
1298
+ return { state: "unavailable", reason: i.takerLabel, remedy: null };
1299
+ }
1300
+ if (i.timedOut) return { state: "unavailable", reason: CAPTIONS_TIMEOUT_REASON, remedy: null };
1301
+ return { state: "starting", reason: null, remedy: null };
1302
+ }
1303
+
1288
1304
  // src/react/hooks.ts
1289
1305
  var EMPTY_NAMES = {};
1290
1306
  var NO_RUNTIME_REMEDY = "Render this inside a <MeetProvider> that has a Supabase client, an organization id and an identity (userId or guestName). The provider stays inert until all three are real, and while it is inert this package's components render nothing rather than a dead control.";
@@ -1329,7 +1345,8 @@ function useRoom() {
1329
1345
  local,
1330
1346
  // An observer (role `viewer`) publishes nothing: no Share screen to press and be refused (MD-16).
1331
1347
  sharingAllowed: snapshot === null || canShareUnder(snapshot.livePolicy, effectiveRole(snapshot), effectiveStanding(snapshot)),
1332
- canShareScreen: (engine?.supportsScreenShare() ?? true) && !isObserverRole(snapshot?.grants.role ?? null) && (snapshot === null || canShareUnder(snapshot.livePolicy, effectiveRole(snapshot), effectiveStanding(snapshot))),
1348
+ canShareScreen: (engine?.supportsScreenShare() ?? true) && !isObserverRole(snapshot?.grants.role ?? null) && // The server's word that sharing was taken away arrives before the rules refresh does: the control goes at once.
1349
+ !(snapshot?.share.state === "ended" && snapshot.share.reason === "not_allowed") && (snapshot === null || canShareUnder(snapshot.livePolicy, effectiveRole(snapshot), effectiveStanding(snapshot))),
1333
1350
  otherPresenter: otherPresenterOf(snapshot),
1334
1351
  join: (0, import_react5.useCallback)(
1335
1352
  async (request) => requireEngine(engine, "join").join(request),
@@ -1673,32 +1690,37 @@ function useCaptions() {
1673
1690
  if (host === null || meeting === null) return;
1674
1691
  void host.ai.requestCaptions({ meetingId: meeting.id });
1675
1692
  }, [host, meeting]);
1693
+ const [timedOut, setTimedOut] = (0, import_react5.useState)(false);
1694
+ const timeoutSeconds = snapshot?.captionsStartTimeoutSeconds ?? 20;
1695
+ const live = taker.state === "transcribing" && taker.present;
1696
+ const [attempt, setAttempt] = (0, import_react5.useState)(0);
1697
+ (0, import_react5.useEffect)(() => {
1698
+ setTimedOut(false);
1699
+ if (!on || live) return void 0;
1700
+ const id = setTimeout(() => setTimedOut(true), timeoutSeconds * 1e3);
1701
+ return () => clearTimeout(id);
1702
+ }, [on, live, timeoutSeconds, attempt]);
1676
1703
  const toggle = (0, import_react5.useCallback)(() => {
1677
1704
  if (host === null) return;
1678
1705
  const next = !on;
1679
1706
  host.store.patch({ captionsOn: next, captionsRefusal: null });
1680
1707
  if (next && taker.state !== "joining" && taker.state !== "transcribing") request();
1681
1708
  }, [host, on, taker.state, request]);
1682
- let state = "off";
1683
- let reason = null;
1684
- let remedy = null;
1685
- if (on) {
1686
- if (taker.state === "transcribing" && taker.present) {
1687
- state = "live";
1688
- } else if (taker.state === "joining") {
1689
- state = "starting";
1690
- } else if (refusal !== null && refusal.reason.length > 0) {
1691
- state = "unavailable";
1692
- reason = refusal.reason;
1693
- remedy = refusal.remedy.length > 0 ? refusal.remedy : null;
1694
- } else if (taker.state === "ended" || taker.state === "handoff_pending") {
1695
- state = "unavailable";
1696
- reason = noteTakerLabel(taker);
1697
- } else {
1698
- state = "starting";
1699
- }
1700
- }
1701
- return { on, state, reason, remedy, toggle };
1709
+ const retry = (0, import_react5.useCallback)(() => {
1710
+ if (host === null) return;
1711
+ host.store.patch({ captionsOn: true, captionsRefusal: null });
1712
+ setAttempt((n) => n + 1);
1713
+ request();
1714
+ }, [host, request]);
1715
+ const view = resolveCaptionsState({
1716
+ on,
1717
+ takerState: taker.state,
1718
+ takerPresent: taker.present,
1719
+ refusal,
1720
+ takerLabel: on ? noteTakerLabel(taker) : "",
1721
+ timedOut
1722
+ });
1723
+ return { on, state: view.state, reason: view.reason, remedy: view.remedy, toggle, retry };
1702
1724
  }
1703
1725
  function useEndMeeting() {
1704
1726
  const host = useMeetHost();
@@ -5457,11 +5479,14 @@ function Captions({ lines = 3 }) {
5457
5479
  const captions = useCaptions();
5458
5480
  if (!captions.on) return null;
5459
5481
  if (captions.state === "unavailable") {
5460
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-meet__captions", role: "status", "data-meet-notice": "captions-unavailable", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__caption", children: [
5461
- "Captions are unavailable. ",
5462
- captions.reason,
5463
- captions.remedy !== null ? ` ${captions.remedy}` : ""
5464
- ] }) });
5482
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__captions", role: "status", "data-meet-notice": "captions-unavailable", children: [
5483
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__caption", children: [
5484
+ (captions.reason ?? "").startsWith("Captions are unavailable") ? "" : "Captions are unavailable. ",
5485
+ captions.reason,
5486
+ captions.remedy !== null ? ` ${captions.remedy}` : ""
5487
+ ] }),
5488
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__caption-retry", onClick: captions.retry, children: "Try again" })
5489
+ ] });
5465
5490
  }
5466
5491
  if (visible.length === 0) {
5467
5492
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-meet__captions", "aria-live": "polite", "aria-label": "Live captions", "data-meet-caption": captions.state, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__caption mx-meet__caption--interim", children: captions.state === "starting" ? "Starting captions. They appear as people speak." : "Captions are on. They appear as people speak." }) });