@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.d.cts CHANGED
@@ -341,6 +341,8 @@ interface UseCaptionsResult {
341
341
  readonly reason: string | null;
342
342
  readonly remedy: string | null;
343
343
  readonly toggle: () => void;
344
+ /** Try again after `unavailable`: asks for captions afresh and restarts the start ceiling. */
345
+ readonly retry: () => void;
344
346
  }
345
347
  declare function useCaptions(): UseCaptionsResult;
346
348
  /**
package/dist/react.d.ts CHANGED
@@ -341,6 +341,8 @@ interface UseCaptionsResult {
341
341
  readonly reason: string | null;
342
342
  readonly remedy: string | null;
343
343
  readonly toggle: () => void;
344
+ /** Try again after `unavailable`: asks for captions afresh and restarts the start ceiling. */
345
+ readonly retry: () => void;
344
346
  }
345
347
  declare function useCaptions(): UseCaptionsResult;
346
348
  /**
package/dist/react.js CHANGED
@@ -151,7 +151,8 @@ var DEFAULT_CONNECTION_POLICY = {
151
151
  autoRejoin: true,
152
152
  autoRejoinMaxAttempts: 5,
153
153
  reconnectGraceSeconds: 30,
154
- videoAutoOffAfterPoorSeconds: 20
154
+ videoAutoOffAfterPoorSeconds: 20,
155
+ captionsStartTimeoutSeconds: 20
155
156
  };
156
157
  function clampInt(value, min, max, fallback) {
157
158
  if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
@@ -166,7 +167,8 @@ function connectionPolicyFrom(raw) {
166
167
  autoRejoin: typeof row["auto_rejoin"] === "boolean" ? row["auto_rejoin"] : d.autoRejoin,
167
168
  autoRejoinMaxAttempts: clampInt(row["auto_rejoin_max_attempts"], 0, 20, d.autoRejoinMaxAttempts),
168
169
  reconnectGraceSeconds: clampInt(row["reconnect_grace_seconds"], 0, 600, d.reconnectGraceSeconds),
169
- videoAutoOffAfterPoorSeconds: clampInt(row["video_auto_off_after_poor_seconds"], 0, 600, d.videoAutoOffAfterPoorSeconds)
170
+ videoAutoOffAfterPoorSeconds: clampInt(row["video_auto_off_after_poor_seconds"], 0, 600, d.videoAutoOffAfterPoorSeconds),
171
+ captionsStartTimeoutSeconds: clampInt(row["captions_start_timeout_seconds"], 1, 600, d.captionsStartTimeoutSeconds)
170
172
  };
171
173
  }
172
174
  var LIVEKIT_BASE_DELAYS_MS = [0, 300, 1200, 2700, 4800, 7e3];
@@ -4030,6 +4032,7 @@ var EMPTY = {
4030
4032
  noteTakerRefusal: null,
4031
4033
  captionsOn: false,
4032
4034
  captionsRefusal: null,
4035
+ captionsStartTimeoutSeconds: 20,
4033
4036
  media: IDLE_MEDIA,
4034
4037
  mediaRegion: IDLE_MEDIA_REGION,
4035
4038
  failure: null,
@@ -4451,7 +4454,10 @@ function createMeetingSession(options) {
4451
4454
  const meeting = store.snapshot().meeting;
4452
4455
  const pass = options.resolvePass?.() ?? null;
4453
4456
  if (meeting === null || pass === null) return;
4454
- if (policyReading) return policyInFlight ?? void 0;
4457
+ if (policyReading) {
4458
+ await (policyInFlight ?? void 0);
4459
+ return readLivePolicy();
4460
+ }
4455
4461
  policyReading = true;
4456
4462
  let done = () => void 0;
4457
4463
  policyInFlight = new Promise((resolve) => done = resolve);
@@ -4472,6 +4478,9 @@ function createMeetingSession(options) {
4472
4478
  if (!samePolicy(snapshot.livePolicy, next) || startedAt !== snapshot.sessionStartedAt) {
4473
4479
  store.patch({ livePolicy: next, sessionStartedAt: startedAt });
4474
4480
  }
4481
+ if (read.consent_given === true && !store.snapshot().recordingConsentGiven) {
4482
+ store.patch({ recordingConsentGiven: true });
4483
+ }
4475
4484
  } catch {
4476
4485
  } finally {
4477
4486
  policyReading = false;
@@ -4776,6 +4785,21 @@ function createMeetingSession(options) {
4776
4785
  stopWaiting,
4777
4786
  readState: readState2,
4778
4787
  readLivePolicy,
4788
+ async acknowledgeConsent() {
4789
+ const meeting = requireMeeting("meet.consent");
4790
+ const pass = options.resolvePass?.() ?? null;
4791
+ const response = await options.api.request({
4792
+ path: MEET_ROUTES.consent,
4793
+ method: "POST",
4794
+ requireAuth: false,
4795
+ operation: "meet.consent",
4796
+ body: { meeting_id: meeting.id },
4797
+ ...pass !== null ? { headers: { [MEETING_PASS_HEADER]: pass } } : {}
4798
+ });
4799
+ if (!response.ok) {
4800
+ throw new MeetError("transport", `meet.consent: the server answered ${response.status}`, "Your agreement was not saved. Try again.");
4801
+ }
4802
+ },
4779
4803
  async readExit(connection, held) {
4780
4804
  const meeting = store.snapshot().meeting;
4781
4805
  const pass = held ?? options.resolvePass?.() ?? null;
@@ -5173,7 +5197,9 @@ function createMediaSupervisor(options) {
5173
5197
  const deviceKind = INPUT_KIND[kind];
5174
5198
  const previous = inUse[deviceKind];
5175
5199
  const track = store.snapshot().media[kind];
5176
- const gone = previous !== null && !isAlias(previous) && before[deviceKind].has(previous) && !now[deviceKind].has(previous);
5200
+ const aliasLabel = previous !== null && isAlias(previous) ? before[deviceKind].get(previous) : void 0;
5201
+ const aliasGone = previous !== null && aliasLabel !== void 0 && aliasLabel !== "" && (!now[deviceKind].has(previous) || now[deviceKind].get(previous) !== aliasLabel);
5202
+ const gone = aliasGone || previous !== null && !isAlias(previous) && before[deviceKind].has(previous) && !now[deviceKind].has(previous);
5177
5203
  if (gone) {
5178
5204
  const from = before[deviceKind].get(previous) ?? "";
5179
5205
  const remaining = [...now[deviceKind].entries()].filter(([id]) => !isAlias(id));
@@ -5198,7 +5224,7 @@ function createMediaSupervisor(options) {
5198
5224
  await driver.setCameraEnabled(true);
5199
5225
  store.setLocalTrack("camera", { enabled: true, unavailable: null });
5200
5226
  }
5201
- setRegion({ notice: { id: ++noticeId, kind, outcome: "switched", from, to: nextLabel } });
5227
+ setRegion({ notice: { id: ++noticeId, kind, outcome: "switched", from, fromId: previous, to: nextLabel } });
5202
5228
  } catch (error) {
5203
5229
  const refused = await unavailable(error, "camera", "meet.camera(fallback)");
5204
5230
  store.setLocalTrack("camera", { enabled: false, unavailable: refused });
@@ -5206,8 +5232,24 @@ function createMediaSupervisor(options) {
5206
5232
  }
5207
5233
  } else {
5208
5234
  const active = driver.activeDevices().audioinput;
5209
- const to = (active !== null ? now.audioinput.get(active) : void 0) ?? now.audioinput.get("default") ?? remaining[0][1];
5210
- setRegion({ notice: { id: ++noticeId, kind, outcome: "switched", from, to } });
5235
+ let to = (active !== null && !aliasGone ? now.audioinput.get(active) : void 0) ?? now.audioinput.get("default");
5236
+ if (to === void 0 || aliasGone) {
5237
+ const [nextId, nextLabel] = remaining[0];
5238
+ try {
5239
+ await driver.switchDevice("audioinput", nextId);
5240
+ if (track.enabled || track.unavailable !== null) {
5241
+ await driver.setMicrophoneEnabled(true);
5242
+ store.setLocalTrack("microphone", { enabled: true, unavailable: null });
5243
+ }
5244
+ to = nextLabel;
5245
+ } catch (error) {
5246
+ const refused = await unavailable(error, "microphone", "meet.microphone(fallback)");
5247
+ store.setLocalTrack("microphone", { enabled: false, unavailable: refused });
5248
+ setRegion({ notice: { id: ++noticeId, kind, outcome: "lost", from, to: null } });
5249
+ continue;
5250
+ }
5251
+ }
5252
+ setRegion({ notice: { id: ++noticeId, kind, outcome: "switched", from, fromId: previous, to } });
5211
5253
  }
5212
5254
  continue;
5213
5255
  }
@@ -5312,6 +5354,21 @@ function createMediaSupervisor(options) {
5312
5354
  },
5313
5355
  dismissNotice() {
5314
5356
  setRegion({ notice: null });
5357
+ },
5358
+ async undoNotice() {
5359
+ const notice = region().notice;
5360
+ setRegion({ notice: null });
5361
+ const driver = options.driver();
5362
+ if (notice === null || notice.outcome !== "switched" || notice.fromId === void 0 || driver === null) return;
5363
+ const list = await snapshotDevices();
5364
+ if (!list[INPUT_KIND[notice.kind]].has(notice.fromId)) return;
5365
+ try {
5366
+ await driver.switchDevice(INPUT_KIND[notice.kind], notice.fromId);
5367
+ readInUse();
5368
+ } catch (error) {
5369
+ const refused = await unavailable(error, notice.kind, "meet.undoDeviceSwitch");
5370
+ store.setLocalTrack(notice.kind, { enabled: false, unavailable: refused });
5371
+ }
5315
5372
  }
5316
5373
  };
5317
5374
  return api;
@@ -5534,11 +5591,10 @@ function moderationText(kind, payload) {
5534
5591
  switch (kind) {
5535
5592
  case "muted": {
5536
5593
  const source = text2(payload["source"]) ?? "microphone";
5537
- const who = payload["by_role"] === "host" || payload["by_role"] === "cohost" ? "the host" : by;
5538
- 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}.`;
5594
+ return source === "microphone" ? `${by} muted you.` : source === "camera" ? `${by} turned off your camera.` : `${by} stopped your screen share.`;
5539
5595
  }
5540
5596
  case "ask-unmute":
5541
- return `${by === "The host" ? "The host" : by} asked you to unmute.`;
5597
+ return `${by} is asking you to unmute.`;
5542
5598
  case "removed":
5543
5599
  return `${by} removed you from this meeting.`;
5544
5600
  default:
@@ -5564,7 +5620,7 @@ function applyModeration(store, payload, now) {
5564
5620
  grants: { ...snapshot.grants, role },
5565
5621
  moderationNotice: {
5566
5622
  kind: "role",
5567
- 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.",
5623
+ 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.",
5568
5624
  at: now()
5569
5625
  }
5570
5626
  });
@@ -6066,7 +6122,14 @@ function createRoomEngine(options) {
6066
6122
  const latest = store.snapshot().meeting;
6067
6123
  if (latest === null) return;
6068
6124
  store.patch({ meeting: { ...latest, endedAt: verified.endedAt } });
6069
- store.dispatch({ type: "ended" }, { reason: "ended" });
6125
+ const connection = current?.connectionIdentity ?? current?.identity ?? store.snapshot().localIdentity ?? null;
6126
+ let why;
6127
+ try {
6128
+ why = options.readExit !== void 0 && connection !== null ? await options.readExit(connection, current?.meetingPass ?? null) : void 0;
6129
+ } catch {
6130
+ why = null;
6131
+ }
6132
+ store.dispatch({ type: "ended" }, { reason: why?.exitReason === "time_limit" ? "time_limit" : "ended" });
6070
6133
  await dropRoom();
6071
6134
  options.onLeft?.("ended");
6072
6135
  } catch (error) {
@@ -6394,6 +6457,7 @@ function createRoomEngine(options) {
6394
6457
  current = token;
6395
6458
  lastServerUrl = token.serverUrl;
6396
6459
  connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;
6460
+ store.patch({ captionsStartTimeoutSeconds: connectionPolicy.captionsStartTimeoutSeconds });
6397
6461
  if (token.leavePolicy !== void 0) store.patch({ leavePolicy: token.leavePolicy });
6398
6462
  store.patch({ canReclaim: token.canReclaim === true });
6399
6463
  store.patch({
@@ -6838,6 +6902,7 @@ function createRoomEngine(options) {
6838
6902
  acceptDeviceOffer: () => media.acceptOffer(),
6839
6903
  dismissDeviceOffer: () => media.dismissOffer(),
6840
6904
  dismissDeviceNotice: () => media.dismissNotice(),
6905
+ undoDeviceNotice: () => media.undoNotice(),
6841
6906
  pin(who) {
6842
6907
  store.patch({ pinned: who, layout: who === null ? "grid" : "speaker" });
6843
6908
  },
@@ -8840,6 +8905,22 @@ function createMeetingRecordCache(options = {}) {
8840
8905
  };
8841
8906
  }
8842
8907
 
8908
+ // src/core/captions-state.ts
8909
+ var CAPTIONS_TIMEOUT_REASON = "Captions are unavailable right now.";
8910
+ function resolveCaptionsState(i) {
8911
+ if (!i.on) return { state: "off", reason: null, remedy: null };
8912
+ if (i.takerState === "transcribing" && i.takerPresent) return { state: "live", reason: null, remedy: null };
8913
+ if (i.takerState === "joining" && !i.timedOut) return { state: "starting", reason: null, remedy: null };
8914
+ if (i.refusal !== null && i.refusal.reason.length > 0) {
8915
+ return { state: "unavailable", reason: i.refusal.reason, remedy: i.refusal.remedy.length > 0 ? i.refusal.remedy : null };
8916
+ }
8917
+ if (i.takerState === "ended" || i.takerState === "handoff_pending") {
8918
+ return { state: "unavailable", reason: i.takerLabel, remedy: null };
8919
+ }
8920
+ if (i.timedOut) return { state: "unavailable", reason: CAPTIONS_TIMEOUT_REASON, remedy: null };
8921
+ return { state: "starting", reason: null, remedy: null };
8922
+ }
8923
+
8843
8924
  // src/core/layout.ts
8844
8925
  var DEFAULT_PAGE_SIZE = 12;
8845
8926
  function orderParticipants(participants, activeSpeakers) {
@@ -8930,7 +9011,8 @@ function useRoom() {
8930
9011
  local,
8931
9012
  // An observer (role `viewer`) publishes nothing: no Share screen to press and be refused (MD-16).
8932
9013
  sharingAllowed: snapshot === null || canShareUnder(snapshot.livePolicy, effectiveRole(snapshot), effectiveStanding(snapshot)),
8933
- canShareScreen: (engine?.supportsScreenShare() ?? true) && !isObserverRole(snapshot?.grants.role ?? null) && (snapshot === null || canShareUnder(snapshot.livePolicy, effectiveRole(snapshot), effectiveStanding(snapshot))),
9014
+ 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.
9015
+ !(snapshot?.share.state === "ended" && snapshot.share.reason === "not_allowed") && (snapshot === null || canShareUnder(snapshot.livePolicy, effectiveRole(snapshot), effectiveStanding(snapshot))),
8934
9016
  otherPresenter: otherPresenterOf(snapshot),
8935
9017
  join: useCallback2(
8936
9018
  async (request) => requireEngine(engine, "join").join(request),
@@ -9327,32 +9409,37 @@ function useCaptions() {
9327
9409
  if (host === null || meeting === null) return;
9328
9410
  void host.ai.requestCaptions({ meetingId: meeting.id });
9329
9411
  }, [host, meeting]);
9412
+ const [timedOut, setTimedOut] = useState3(false);
9413
+ const timeoutSeconds = snapshot?.captionsStartTimeoutSeconds ?? 20;
9414
+ const live = taker.state === "transcribing" && taker.present;
9415
+ const [attempt, setAttempt] = useState3(0);
9416
+ useEffect3(() => {
9417
+ setTimedOut(false);
9418
+ if (!on || live) return void 0;
9419
+ const id = setTimeout(() => setTimedOut(true), timeoutSeconds * 1e3);
9420
+ return () => clearTimeout(id);
9421
+ }, [on, live, timeoutSeconds, attempt]);
9330
9422
  const toggle = useCallback2(() => {
9331
9423
  if (host === null) return;
9332
9424
  const next = !on;
9333
9425
  host.store.patch({ captionsOn: next, captionsRefusal: null });
9334
9426
  if (next && taker.state !== "joining" && taker.state !== "transcribing") request();
9335
9427
  }, [host, on, taker.state, request]);
9336
- let state = "off";
9337
- let reason = null;
9338
- let remedy = null;
9339
- if (on) {
9340
- if (taker.state === "transcribing" && taker.present) {
9341
- state = "live";
9342
- } else if (taker.state === "joining") {
9343
- state = "starting";
9344
- } else if (refusal2 !== null && refusal2.reason.length > 0) {
9345
- state = "unavailable";
9346
- reason = refusal2.reason;
9347
- remedy = refusal2.remedy.length > 0 ? refusal2.remedy : null;
9348
- } else if (taker.state === "ended" || taker.state === "handoff_pending") {
9349
- state = "unavailable";
9350
- reason = noteTakerLabel(taker);
9351
- } else {
9352
- state = "starting";
9353
- }
9354
- }
9355
- return { on, state, reason, remedy, toggle };
9428
+ const retry = useCallback2(() => {
9429
+ if (host === null) return;
9430
+ host.store.patch({ captionsOn: true, captionsRefusal: null });
9431
+ setAttempt((n) => n + 1);
9432
+ request();
9433
+ }, [host, request]);
9434
+ const view = resolveCaptionsState({
9435
+ on,
9436
+ takerState: taker.state,
9437
+ takerPresent: taker.present,
9438
+ refusal: refusal2,
9439
+ takerLabel: on ? noteTakerLabel(taker) : "",
9440
+ timedOut
9441
+ });
9442
+ return { on, state: view.state, reason: view.reason, remedy: view.remedy, toggle, retry };
9356
9443
  }
9357
9444
  function useEndMeeting() {
9358
9445
  const host = useMeetHost();
@@ -9410,6 +9497,7 @@ function useMediaSupervisor() {
9410
9497
  acceptOffer: useCallback2(async () => engine?.acceptDeviceOffer(), [engine]),
9411
9498
  dismissOffer: useCallback2(() => engine?.dismissDeviceOffer(), [engine]),
9412
9499
  dismissNotice: useCallback2(() => engine?.dismissDeviceNotice(), [engine]),
9500
+ undoNotice: useCallback2(async () => engine?.undoDeviceNotice(), [engine]),
9413
9501
  retry: useCallback2(
9414
9502
  async (kind) => kind === "microphone" ? engine?.setMicrophoneEnabled(true) : engine?.setCameraEnabled(true),
9415
9503
  [engine]
@@ -9879,19 +9967,19 @@ function useLivePolicy() {
9879
9967
  const host = useMeetHost();
9880
9968
  const pending = consentPending(policy, role, snapshot?.recording === "recording", snapshot?.recordingConsentGiven === true);
9881
9969
  const viewOnly = pending && policy.recordingConsent === "view_only_until_agree";
9882
- const idle = snapshot !== null && snapshot.recording !== "recording" && snapshot.recording !== "starting" && snapshot.recording !== "stopping";
9883
- const given = snapshot?.recordingConsentGiven === true;
9884
9970
  const store = host?.store ?? null;
9885
- useEffect3(() => {
9886
- if (idle && given) store?.patch({ recordingConsentGiven: false });
9887
- }, [idle, given, store]);
9888
9971
  return {
9889
9972
  canChat: canChat(policy, role),
9890
9973
  chatMode: policy.chatMode,
9891
9974
  micLocked: isLockedFor(policy, role, "microphone") || viewOnly,
9892
9975
  cameraLocked: isLockedFor(policy, role, "camera") || viewOnly,
9893
9976
  consent: pending ? policy.recordingConsent === "ok_or_leave" ? "ok_or_leave" : "view_only_until_agree" : null,
9894
- agree: () => store?.patch({ recordingConsentGiven: true }),
9977
+ // The person's agreement is recorded by the SERVER (per run); the screen follows at once and
9978
+ // takes it back if the server did not save it.
9979
+ agree: () => {
9980
+ store?.patch({ recordingConsentGiven: true });
9981
+ void host?.meetings.acknowledgeConsent().catch(() => store?.patch({ recordingConsentGiven: false }));
9982
+ },
9895
9983
  minutesLeft: left,
9896
9984
  timeWarning: left !== null && left <= policy.timeLimitWarningMinutes
9897
9985
  };
@@ -12690,7 +12778,7 @@ function SpeakingWhileMutedNudge() {
12690
12778
  }
12691
12779
  function MediaNotices() {
12692
12780
  const media = useLocalMedia();
12693
- const { region, where, enableAudio, acceptOffer, dismissOffer, dismissNotice, retry } = useMediaSupervisor();
12781
+ const { region, where, enableAudio, acceptOffer, dismissOffer, dismissNotice, undoNotice, retry } = useMediaSupervisor();
12694
12782
  const [fixing, setFixing] = useState5(null);
12695
12783
  const chips = ["microphone", "camera"].flatMap((kind) => {
12696
12784
  const reason = deviceUnavailableReason(media[kind], region.permission[kind]);
@@ -12755,9 +12843,10 @@ function MediaNotices() {
12755
12843
  {
12756
12844
  className: "mx-meet__media-pill",
12757
12845
  role: "status",
12758
- ...notice.outcome === "lost" ? { "data-meet-notice": "device-lost" } : {},
12846
+ "data-meet-notice": notice.outcome === "lost" ? "device-lost" : "device-switched",
12759
12847
  children: [
12760
- /* @__PURE__ */ jsx6("span", { children: notice.outcome === "switched" ? `${notice.from || name(notice.kind)} disconnected \xB7 now using ${notice.to || "the default"}` : `${notice.from || name(notice.kind)} disconnected` }),
12848
+ /* @__PURE__ */ jsx6("span", { children: notice.outcome === "switched" ? `${notice.from || name(notice.kind)} disconnected \xB7 switched to ${notice.to || "the default"}` : `${notice.from || name(notice.kind)} disconnected` }),
12849
+ notice.outcome === "switched" ? /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: () => void undoNotice(), children: "Undo" }) : null,
12761
12850
  /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__toast-close", "aria-label": "Dismiss", onClick: dismissNotice, children: /* @__PURE__ */ jsx6(CloseIcon, { width: 14, height: 14 }) })
12762
12851
  ]
12763
12852
  }
@@ -12810,7 +12899,7 @@ function MeetingOverlays() {
12810
12899
  live.minutesLeft,
12811
12900
  " ",
12812
12901
  live.minutesLeft === 1 ? "minute" : "minutes",
12813
- " (time limit)."
12902
+ "."
12814
12903
  ] }) : null,
12815
12904
  notice !== null && notice.kind !== "removed" ? (
12816
12905
  // A host's request ("the host is asking you to unmute"), not an error:
@@ -13869,11 +13958,14 @@ function Captions({ lines = 3 }) {
13869
13958
  const captions = useCaptions();
13870
13959
  if (!captions.on) return null;
13871
13960
  if (captions.state === "unavailable") {
13872
- return /* @__PURE__ */ jsx6("div", { className: "mx-meet__captions", role: "status", "data-meet-notice": "captions-unavailable", children: /* @__PURE__ */ jsxs4("p", { className: "mx-meet__caption", children: [
13873
- "Captions are unavailable. ",
13874
- captions.reason,
13875
- captions.remedy !== null ? ` ${captions.remedy}` : ""
13876
- ] }) });
13961
+ return /* @__PURE__ */ jsxs4("div", { className: "mx-meet__captions", role: "status", "data-meet-notice": "captions-unavailable", children: [
13962
+ /* @__PURE__ */ jsxs4("p", { className: "mx-meet__caption", children: [
13963
+ (captions.reason ?? "").startsWith("Captions are unavailable") ? "" : "Captions are unavailable. ",
13964
+ captions.reason,
13965
+ captions.remedy !== null ? ` ${captions.remedy}` : ""
13966
+ ] }),
13967
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__caption-retry", onClick: captions.retry, children: "Try again" })
13968
+ ] });
13877
13969
  }
13878
13970
  if (visible.length === 0) {
13879
13971
  return /* @__PURE__ */ jsx6("div", { className: "mx-meet__captions", "aria-live": "polite", "aria-label": "Live captions", "data-meet-caption": captions.state, children: /* @__PURE__ */ jsx6("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." }) });