@ai-matrx/meet 0.11.49 → 0.11.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.cjs CHANGED
@@ -491,7 +491,8 @@ var DEFAULT_CONNECTION_POLICY = {
491
491
  autoRejoin: true,
492
492
  autoRejoinMaxAttempts: 5,
493
493
  reconnectGraceSeconds: 30,
494
- videoAutoOffAfterPoorSeconds: 20
494
+ videoAutoOffAfterPoorSeconds: 20,
495
+ captionsStartTimeoutSeconds: 20
495
496
  };
496
497
  function clampInt(value, min, max, fallback) {
497
498
  if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
@@ -506,7 +507,8 @@ function connectionPolicyFrom(raw) {
506
507
  autoRejoin: typeof row["auto_rejoin"] === "boolean" ? row["auto_rejoin"] : d.autoRejoin,
507
508
  autoRejoinMaxAttempts: clampInt(row["auto_rejoin_max_attempts"], 0, 20, d.autoRejoinMaxAttempts),
508
509
  reconnectGraceSeconds: clampInt(row["reconnect_grace_seconds"], 0, 600, d.reconnectGraceSeconds),
509
- videoAutoOffAfterPoorSeconds: clampInt(row["video_auto_off_after_poor_seconds"], 0, 600, d.videoAutoOffAfterPoorSeconds)
510
+ videoAutoOffAfterPoorSeconds: clampInt(row["video_auto_off_after_poor_seconds"], 0, 600, d.videoAutoOffAfterPoorSeconds),
511
+ captionsStartTimeoutSeconds: clampInt(row["captions_start_timeout_seconds"], 1, 600, d.captionsStartTimeoutSeconds)
510
512
  };
511
513
  }
512
514
  var LIVEKIT_BASE_DELAYS_MS = [0, 300, 1200, 2700, 4800, 7e3];
@@ -4357,6 +4359,7 @@ var EMPTY = {
4357
4359
  noteTakerRefusal: null,
4358
4360
  captionsOn: false,
4359
4361
  captionsRefusal: null,
4362
+ captionsStartTimeoutSeconds: 20,
4360
4363
  media: IDLE_MEDIA,
4361
4364
  mediaRegion: IDLE_MEDIA_REGION,
4362
4365
  failure: null,
@@ -4778,7 +4781,10 @@ function createMeetingSession(options) {
4778
4781
  const meeting = store.snapshot().meeting;
4779
4782
  const pass = options.resolvePass?.() ?? null;
4780
4783
  if (meeting === null || pass === null) return;
4781
- if (policyReading) return policyInFlight ?? void 0;
4784
+ if (policyReading) {
4785
+ await (policyInFlight ?? void 0);
4786
+ return readLivePolicy();
4787
+ }
4782
4788
  policyReading = true;
4783
4789
  let done = () => void 0;
4784
4790
  policyInFlight = new Promise((resolve) => done = resolve);
@@ -4799,6 +4805,9 @@ function createMeetingSession(options) {
4799
4805
  if (!samePolicy(snapshot.livePolicy, next) || startedAt !== snapshot.sessionStartedAt) {
4800
4806
  store.patch({ livePolicy: next, sessionStartedAt: startedAt });
4801
4807
  }
4808
+ if (read.consent_given === true && !store.snapshot().recordingConsentGiven) {
4809
+ store.patch({ recordingConsentGiven: true });
4810
+ }
4802
4811
  } catch {
4803
4812
  } finally {
4804
4813
  policyReading = false;
@@ -5103,6 +5112,21 @@ function createMeetingSession(options) {
5103
5112
  stopWaiting,
5104
5113
  readState: readState2,
5105
5114
  readLivePolicy,
5115
+ async acknowledgeConsent() {
5116
+ const meeting = requireMeeting("meet.consent");
5117
+ const pass = options.resolvePass?.() ?? null;
5118
+ const response = await options.api.request({
5119
+ path: MEET_ROUTES.consent,
5120
+ method: "POST",
5121
+ requireAuth: false,
5122
+ operation: "meet.consent",
5123
+ body: { meeting_id: meeting.id },
5124
+ ...pass !== null ? { headers: { [MEETING_PASS_HEADER]: pass } } : {}
5125
+ });
5126
+ if (!response.ok) {
5127
+ throw new MeetError("transport", `meet.consent: the server answered ${response.status}`, "Your agreement was not saved. Try again.");
5128
+ }
5129
+ },
5106
5130
  async readExit(connection, held) {
5107
5131
  const meeting = store.snapshot().meeting;
5108
5132
  const pass = held ?? options.resolvePass?.() ?? null;
@@ -5498,7 +5522,9 @@ function createMediaSupervisor(options) {
5498
5522
  const deviceKind = INPUT_KIND[kind];
5499
5523
  const previous = inUse[deviceKind];
5500
5524
  const track = store.snapshot().media[kind];
5501
- const gone = previous !== null && !isAlias(previous) && before[deviceKind].has(previous) && !now[deviceKind].has(previous);
5525
+ const aliasLabel = previous !== null && isAlias(previous) ? before[deviceKind].get(previous) : void 0;
5526
+ const aliasGone = previous !== null && aliasLabel !== void 0 && aliasLabel !== "" && (!now[deviceKind].has(previous) || now[deviceKind].get(previous) !== aliasLabel);
5527
+ const gone = aliasGone || previous !== null && !isAlias(previous) && before[deviceKind].has(previous) && !now[deviceKind].has(previous);
5502
5528
  if (gone) {
5503
5529
  const from = before[deviceKind].get(previous) ?? "";
5504
5530
  const remaining = [...now[deviceKind].entries()].filter(([id]) => !isAlias(id));
@@ -5523,7 +5549,7 @@ function createMediaSupervisor(options) {
5523
5549
  await driver.setCameraEnabled(true);
5524
5550
  store.setLocalTrack("camera", { enabled: true, unavailable: null });
5525
5551
  }
5526
- setRegion({ notice: { id: ++noticeId, kind, outcome: "switched", from, to: nextLabel } });
5552
+ setRegion({ notice: { id: ++noticeId, kind, outcome: "switched", from, fromId: previous, to: nextLabel } });
5527
5553
  } catch (error) {
5528
5554
  const refused = await unavailable(error, "camera", "meet.camera(fallback)");
5529
5555
  store.setLocalTrack("camera", { enabled: false, unavailable: refused });
@@ -5531,8 +5557,24 @@ function createMediaSupervisor(options) {
5531
5557
  }
5532
5558
  } else {
5533
5559
  const active = driver.activeDevices().audioinput;
5534
- const to = (active !== null ? now.audioinput.get(active) : void 0) ?? now.audioinput.get("default") ?? remaining[0][1];
5535
- setRegion({ notice: { id: ++noticeId, kind, outcome: "switched", from, to } });
5560
+ let to = (active !== null && !aliasGone ? now.audioinput.get(active) : void 0) ?? now.audioinput.get("default");
5561
+ if (to === void 0 || aliasGone) {
5562
+ const [nextId, nextLabel] = remaining[0];
5563
+ try {
5564
+ await driver.switchDevice("audioinput", nextId);
5565
+ if (track.enabled || track.unavailable !== null) {
5566
+ await driver.setMicrophoneEnabled(true);
5567
+ store.setLocalTrack("microphone", { enabled: true, unavailable: null });
5568
+ }
5569
+ to = nextLabel;
5570
+ } catch (error) {
5571
+ const refused = await unavailable(error, "microphone", "meet.microphone(fallback)");
5572
+ store.setLocalTrack("microphone", { enabled: false, unavailable: refused });
5573
+ setRegion({ notice: { id: ++noticeId, kind, outcome: "lost", from, to: null } });
5574
+ continue;
5575
+ }
5576
+ }
5577
+ setRegion({ notice: { id: ++noticeId, kind, outcome: "switched", from, fromId: previous, to } });
5536
5578
  }
5537
5579
  continue;
5538
5580
  }
@@ -5637,6 +5679,21 @@ function createMediaSupervisor(options) {
5637
5679
  },
5638
5680
  dismissNotice() {
5639
5681
  setRegion({ notice: null });
5682
+ },
5683
+ async undoNotice() {
5684
+ const notice = region().notice;
5685
+ setRegion({ notice: null });
5686
+ const driver = options.driver();
5687
+ if (notice === null || notice.outcome !== "switched" || notice.fromId === void 0 || driver === null) return;
5688
+ const list = await snapshotDevices();
5689
+ if (!list[INPUT_KIND[notice.kind]].has(notice.fromId)) return;
5690
+ try {
5691
+ await driver.switchDevice(INPUT_KIND[notice.kind], notice.fromId);
5692
+ readInUse();
5693
+ } catch (error) {
5694
+ const refused = await unavailable(error, notice.kind, "meet.undoDeviceSwitch");
5695
+ store.setLocalTrack(notice.kind, { enabled: false, unavailable: refused });
5696
+ }
5640
5697
  }
5641
5698
  };
5642
5699
  return api;
@@ -5859,11 +5916,10 @@ function moderationText(kind, payload) {
5859
5916
  switch (kind) {
5860
5917
  case "muted": {
5861
5918
  const source = text2(payload["source"]) ?? "microphone";
5862
- const who = payload["by_role"] === "host" || payload["by_role"] === "cohost" ? "the host" : by;
5863
- return source === "microphone" ? `You've been muted by ${who}.` : source === "camera" ? `Your camera was turned off by ${who}.` : `Your screen share was stopped by ${who}.`;
5919
+ return source === "microphone" ? `${by} muted you.` : source === "camera" ? `${by} turned off your camera.` : `${by} stopped your screen share.`;
5864
5920
  }
5865
5921
  case "ask-unmute":
5866
- return `${by === "The host" ? "The host" : by} asked you to unmute.`;
5922
+ return `${by} is asking you to unmute.`;
5867
5923
  case "removed":
5868
5924
  return `${by} removed you from this meeting.`;
5869
5925
  default:
@@ -5889,7 +5945,7 @@ function applyModeration(store, payload, now) {
5889
5945
  grants: { ...snapshot.grants, role },
5890
5946
  moderationNotice: {
5891
5947
  kind: "role",
5892
- text: role === "host" ? "You are now the host." : role === "cohost" ? "You're now a co-host." : role === "presenter" ? "You're now a presenter." : snapshot.grants.role === "presenter" ? "You're now a participant: you can no longer present." : "You're now a participant: you are no longer a co-host.",
5948
+ text: role === "host" ? "You are now the host." : role === "cohost" ? "You are now a co-host." : role === "presenter" ? "You are now a presenter." : snapshot.grants.role === "presenter" ? "You are now a participant: you can no longer present." : "You are no longer a co-host.",
5893
5949
  at: now()
5894
5950
  }
5895
5951
  });
@@ -6391,7 +6447,14 @@ function createRoomEngine(options) {
6391
6447
  const latest = store.snapshot().meeting;
6392
6448
  if (latest === null) return;
6393
6449
  store.patch({ meeting: { ...latest, endedAt: verified.endedAt } });
6394
- store.dispatch({ type: "ended" }, { reason: "ended" });
6450
+ const connection = current?.connectionIdentity ?? current?.identity ?? store.snapshot().localIdentity ?? null;
6451
+ let why;
6452
+ try {
6453
+ why = options.readExit !== void 0 && connection !== null ? await options.readExit(connection, current?.meetingPass ?? null) : void 0;
6454
+ } catch {
6455
+ why = null;
6456
+ }
6457
+ store.dispatch({ type: "ended" }, { reason: why?.exitReason === "time_limit" ? "time_limit" : "ended" });
6395
6458
  await dropRoom();
6396
6459
  options.onLeft?.("ended");
6397
6460
  } catch (error) {
@@ -6719,6 +6782,7 @@ function createRoomEngine(options) {
6719
6782
  current = token;
6720
6783
  lastServerUrl = token.serverUrl;
6721
6784
  connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;
6785
+ store.patch({ captionsStartTimeoutSeconds: connectionPolicy.captionsStartTimeoutSeconds });
6722
6786
  if (token.leavePolicy !== void 0) store.patch({ leavePolicy: token.leavePolicy });
6723
6787
  store.patch({ canReclaim: token.canReclaim === true });
6724
6788
  store.patch({
@@ -7163,6 +7227,7 @@ function createRoomEngine(options) {
7163
7227
  acceptDeviceOffer: () => media.acceptOffer(),
7164
7228
  dismissDeviceOffer: () => media.dismissOffer(),
7165
7229
  dismissDeviceNotice: () => media.dismissNotice(),
7230
+ undoDeviceNotice: () => media.undoNotice(),
7166
7231
  pin(who) {
7167
7232
  store.patch({ pinned: who, layout: who === null ? "grid" : "speaker" });
7168
7233
  },
@@ -9165,6 +9230,22 @@ function createMeetingRecordCache(options = {}) {
9165
9230
  };
9166
9231
  }
9167
9232
 
9233
+ // src/core/captions-state.ts
9234
+ var CAPTIONS_TIMEOUT_REASON = "Captions are unavailable right now.";
9235
+ function resolveCaptionsState(i) {
9236
+ if (!i.on) return { state: "off", reason: null, remedy: null };
9237
+ if (i.takerState === "transcribing" && i.takerPresent) return { state: "live", reason: null, remedy: null };
9238
+ if (i.takerState === "joining" && !i.timedOut) return { state: "starting", reason: null, remedy: null };
9239
+ if (i.refusal !== null && i.refusal.reason.length > 0) {
9240
+ return { state: "unavailable", reason: i.refusal.reason, remedy: i.refusal.remedy.length > 0 ? i.refusal.remedy : null };
9241
+ }
9242
+ if (i.takerState === "ended" || i.takerState === "handoff_pending") {
9243
+ return { state: "unavailable", reason: i.takerLabel, remedy: null };
9244
+ }
9245
+ if (i.timedOut) return { state: "unavailable", reason: CAPTIONS_TIMEOUT_REASON, remedy: null };
9246
+ return { state: "starting", reason: null, remedy: null };
9247
+ }
9248
+
9168
9249
  // src/core/layout.ts
9169
9250
  var DEFAULT_PAGE_SIZE = 12;
9170
9251
  function orderParticipants(participants, activeSpeakers) {
@@ -9255,7 +9336,8 @@ function useRoom() {
9255
9336
  local,
9256
9337
  // An observer (role `viewer`) publishes nothing: no Share screen to press and be refused (MD-16).
9257
9338
  sharingAllowed: snapshot === null || canShareUnder(snapshot.livePolicy, effectiveRole(snapshot), effectiveStanding(snapshot)),
9258
- canShareScreen: (engine?.supportsScreenShare() ?? true) && !isObserverRole(snapshot?.grants.role ?? null) && (snapshot === null || canShareUnder(snapshot.livePolicy, effectiveRole(snapshot), effectiveStanding(snapshot))),
9339
+ 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.
9340
+ !(snapshot?.share.state === "ended" && snapshot.share.reason === "not_allowed") && (snapshot === null || canShareUnder(snapshot.livePolicy, effectiveRole(snapshot), effectiveStanding(snapshot))),
9259
9341
  otherPresenter: otherPresenterOf(snapshot),
9260
9342
  join: (0, import_react4.useCallback)(
9261
9343
  async (request) => requireEngine(engine, "join").join(request),
@@ -9652,32 +9734,37 @@ function useCaptions() {
9652
9734
  if (host === null || meeting === null) return;
9653
9735
  void host.ai.requestCaptions({ meetingId: meeting.id });
9654
9736
  }, [host, meeting]);
9737
+ const [timedOut, setTimedOut] = (0, import_react4.useState)(false);
9738
+ const timeoutSeconds = snapshot?.captionsStartTimeoutSeconds ?? 20;
9739
+ const live = taker.state === "transcribing" && taker.present;
9740
+ const [attempt, setAttempt] = (0, import_react4.useState)(0);
9741
+ (0, import_react4.useEffect)(() => {
9742
+ setTimedOut(false);
9743
+ if (!on || live) return void 0;
9744
+ const id = setTimeout(() => setTimedOut(true), timeoutSeconds * 1e3);
9745
+ return () => clearTimeout(id);
9746
+ }, [on, live, timeoutSeconds, attempt]);
9655
9747
  const toggle = (0, import_react4.useCallback)(() => {
9656
9748
  if (host === null) return;
9657
9749
  const next = !on;
9658
9750
  host.store.patch({ captionsOn: next, captionsRefusal: null });
9659
9751
  if (next && taker.state !== "joining" && taker.state !== "transcribing") request();
9660
9752
  }, [host, on, taker.state, request]);
9661
- let state = "off";
9662
- let reason = null;
9663
- let remedy = null;
9664
- if (on) {
9665
- if (taker.state === "transcribing" && taker.present) {
9666
- state = "live";
9667
- } else if (taker.state === "joining") {
9668
- state = "starting";
9669
- } else if (refusal2 !== null && refusal2.reason.length > 0) {
9670
- state = "unavailable";
9671
- reason = refusal2.reason;
9672
- remedy = refusal2.remedy.length > 0 ? refusal2.remedy : null;
9673
- } else if (taker.state === "ended" || taker.state === "handoff_pending") {
9674
- state = "unavailable";
9675
- reason = noteTakerLabel(taker);
9676
- } else {
9677
- state = "starting";
9678
- }
9679
- }
9680
- return { on, state, reason, remedy, toggle };
9753
+ const retry = (0, import_react4.useCallback)(() => {
9754
+ if (host === null) return;
9755
+ host.store.patch({ captionsOn: true, captionsRefusal: null });
9756
+ setAttempt((n) => n + 1);
9757
+ request();
9758
+ }, [host, request]);
9759
+ const view = resolveCaptionsState({
9760
+ on,
9761
+ takerState: taker.state,
9762
+ takerPresent: taker.present,
9763
+ refusal: refusal2,
9764
+ takerLabel: on ? noteTakerLabel(taker) : "",
9765
+ timedOut
9766
+ });
9767
+ return { on, state: view.state, reason: view.reason, remedy: view.remedy, toggle, retry };
9681
9768
  }
9682
9769
  function useEndMeeting() {
9683
9770
  const host = useMeetHost();
@@ -9735,6 +9822,7 @@ function useMediaSupervisor() {
9735
9822
  acceptOffer: (0, import_react4.useCallback)(async () => engine?.acceptDeviceOffer(), [engine]),
9736
9823
  dismissOffer: (0, import_react4.useCallback)(() => engine?.dismissDeviceOffer(), [engine]),
9737
9824
  dismissNotice: (0, import_react4.useCallback)(() => engine?.dismissDeviceNotice(), [engine]),
9825
+ undoNotice: (0, import_react4.useCallback)(async () => engine?.undoDeviceNotice(), [engine]),
9738
9826
  retry: (0, import_react4.useCallback)(
9739
9827
  async (kind) => kind === "microphone" ? engine?.setMicrophoneEnabled(true) : engine?.setCameraEnabled(true),
9740
9828
  [engine]
@@ -10204,19 +10292,19 @@ function useLivePolicy() {
10204
10292
  const host = useMeetHost();
10205
10293
  const pending = consentPending(policy, role, snapshot?.recording === "recording", snapshot?.recordingConsentGiven === true);
10206
10294
  const viewOnly = pending && policy.recordingConsent === "view_only_until_agree";
10207
- const idle = snapshot !== null && snapshot.recording !== "recording" && snapshot.recording !== "starting" && snapshot.recording !== "stopping";
10208
- const given = snapshot?.recordingConsentGiven === true;
10209
10295
  const store = host?.store ?? null;
10210
- (0, import_react4.useEffect)(() => {
10211
- if (idle && given) store?.patch({ recordingConsentGiven: false });
10212
- }, [idle, given, store]);
10213
10296
  return {
10214
10297
  canChat: canChat(policy, role),
10215
10298
  chatMode: policy.chatMode,
10216
10299
  micLocked: isLockedFor(policy, role, "microphone") || viewOnly,
10217
10300
  cameraLocked: isLockedFor(policy, role, "camera") || viewOnly,
10218
10301
  consent: pending ? policy.recordingConsent === "ok_or_leave" ? "ok_or_leave" : "view_only_until_agree" : null,
10219
- agree: () => store?.patch({ recordingConsentGiven: true }),
10302
+ // The person's agreement is recorded by the SERVER (per run); the screen follows at once and
10303
+ // takes it back if the server did not save it.
10304
+ agree: () => {
10305
+ store?.patch({ recordingConsentGiven: true });
10306
+ void host?.meetings.acknowledgeConsent().catch(() => store?.patch({ recordingConsentGiven: false }));
10307
+ },
10220
10308
  minutesLeft: left,
10221
10309
  timeWarning: left !== null && left <= policy.timeLimitWarningMinutes
10222
10310
  };
@@ -13015,7 +13103,7 @@ function SpeakingWhileMutedNudge() {
13015
13103
  }
13016
13104
  function MediaNotices() {
13017
13105
  const media = useLocalMedia();
13018
- const { region, where, enableAudio, acceptOffer, dismissOffer, dismissNotice, retry } = useMediaSupervisor();
13106
+ const { region, where, enableAudio, acceptOffer, dismissOffer, dismissNotice, undoNotice, retry } = useMediaSupervisor();
13019
13107
  const [fixing, setFixing] = (0, import_react10.useState)(null);
13020
13108
  const chips = ["microphone", "camera"].flatMap((kind) => {
13021
13109
  const reason = deviceUnavailableReason(media[kind], region.permission[kind]);
@@ -13080,9 +13168,10 @@ function MediaNotices() {
13080
13168
  {
13081
13169
  className: "mx-meet__media-pill",
13082
13170
  role: "status",
13083
- ...notice.outcome === "lost" ? { "data-meet-notice": "device-lost" } : {},
13171
+ "data-meet-notice": notice.outcome === "lost" ? "device-lost" : "device-switched",
13084
13172
  children: [
13085
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: notice.outcome === "switched" ? `${notice.from || name(notice.kind)} disconnected \xB7 now using ${notice.to || "the default"}` : `${notice.from || name(notice.kind)} disconnected` }),
13173
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: notice.outcome === "switched" ? `${notice.from || name(notice.kind)} disconnected \xB7 switched to ${notice.to || "the default"}` : `${notice.from || name(notice.kind)} disconnected` }),
13174
+ notice.outcome === "switched" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__link", onClick: () => void undoNotice(), children: "Undo" }) : null,
13086
13175
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__toast-close", "aria-label": "Dismiss", onClick: dismissNotice, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CloseIcon, { width: 14, height: 14 }) })
13087
13176
  ]
13088
13177
  }
@@ -13135,7 +13224,7 @@ function MeetingOverlays() {
13135
13224
  live.minutesLeft,
13136
13225
  " ",
13137
13226
  live.minutesLeft === 1 ? "minute" : "minutes",
13138
- " (time limit)."
13227
+ "."
13139
13228
  ] }) : null,
13140
13229
  notice !== null && notice.kind !== "removed" ? (
13141
13230
  // A host's request ("the host is asking you to unmute"), not an error:
@@ -14194,11 +14283,14 @@ function Captions({ lines = 3 }) {
14194
14283
  const captions = useCaptions();
14195
14284
  if (!captions.on) return null;
14196
14285
  if (captions.state === "unavailable") {
14197
- 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: [
14198
- "Captions are unavailable. ",
14199
- captions.reason,
14200
- captions.remedy !== null ? ` ${captions.remedy}` : ""
14201
- ] }) });
14286
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__captions", role: "status", "data-meet-notice": "captions-unavailable", children: [
14287
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__caption", children: [
14288
+ (captions.reason ?? "").startsWith("Captions are unavailable") ? "" : "Captions are unavailable. ",
14289
+ captions.reason,
14290
+ captions.remedy !== null ? ` ${captions.remedy}` : ""
14291
+ ] }),
14292
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__caption-retry", onClick: captions.retry, children: "Try again" })
14293
+ ] });
14202
14294
  }
14203
14295
  if (visible.length === 0) {
14204
14296
  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." }) });