@ai-matrx/meet 0.11.42 → 0.11.44

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.js CHANGED
@@ -786,6 +786,8 @@ var MEET_ROUTES = {
786
786
  noteTakerStop: "/api/v1/meet/agent/stop",
787
787
  /** In-meeting Q&A. Streams NDJSON; body is `{meeting_id, question}` only. */
788
788
  intelligenceAsk: "/api/v1/meet/intelligence/ask",
789
+ /** Anyone in the meeting (guests with the room pass included) asks for captions for themselves. */
790
+ noteTakerCaptions: "/api/v1/meet/agent/captions",
789
791
  /** A host ends the meeting for everyone. Served by MRI-A6. */
790
792
  meetingEnd: "/api/v1/meet/end",
791
793
  /**
@@ -932,7 +934,8 @@ function parseRoomToken(body, roomName) {
932
934
  breakoutRoomId: readString(payload, "breakout_room_id") ?? null,
933
935
  observersVisibleTo: readString(payload, "observers_visible_to") === "hosts" ? "hosts" : "everyone",
934
936
  connectionPolicy: connectionPolicyFrom(payload["connection_policy"]),
935
- leavePolicy: leavePolicyFrom(payload["leave_policy"])
937
+ leavePolicy: leavePolicyFrom(payload["leave_policy"]),
938
+ canReclaim: payload["can_reclaim"] === true
936
939
  };
937
940
  }
938
941
  function createTokenClient(options) {
@@ -1394,6 +1397,42 @@ function createMeetAi(options) {
1394
1397
  },
1395
1398
  "meet.ai.startNoteTaker"
1396
1399
  ),
1400
+ requestCaptions: async ({ meetingId }) => {
1401
+ const roomToken = options.resolveRoomToken?.() ?? null;
1402
+ const operation = "meet.ai.requestCaptions";
1403
+ try {
1404
+ const response = await send({
1405
+ path: MEET_ROUTES.noteTakerCaptions,
1406
+ body: { meeting_id: meetingId },
1407
+ operation,
1408
+ requireAuth: roomToken === null,
1409
+ ...roomToken !== null ? { headers: { [MEETING_PASS_HEADER]: roomToken } } : {}
1410
+ });
1411
+ if (!response.ok) {
1412
+ options.store.patch({ captionsRefusal: await refusalFor(response, operation) });
1413
+ return;
1414
+ }
1415
+ let payload = null;
1416
+ try {
1417
+ payload = await response.json();
1418
+ } catch {
1419
+ payload = null;
1420
+ }
1421
+ const row = typeof payload === "object" && payload !== null ? payload : {};
1422
+ const detail = typeof row["detail"] === "string" ? row["detail"] : "";
1423
+ options.store.patch({
1424
+ captionsRefusal: row["dispatched"] === true ? null : { status: 200, reason: detail, remedy: "" }
1425
+ });
1426
+ } catch (error) {
1427
+ options.store.patch({
1428
+ captionsRefusal: {
1429
+ status: 0,
1430
+ reason: error.message,
1431
+ remedy: error.remedy ?? ""
1432
+ }
1433
+ });
1434
+ }
1435
+ },
1397
1436
  stopNoteTaker: ({ meetingId }) => dispatch(
1398
1437
  MEET_ROUTES.noteTakerStop,
1399
1438
  { meeting_id: meetingId },
@@ -2380,12 +2419,63 @@ function recordingLabel(state) {
2380
2419
  }
2381
2420
  }
2382
2421
 
2422
+ // src/core/live-policy.ts
2423
+ var DEFAULT_LIVE_POLICY = {
2424
+ chatMode: "everyone",
2425
+ avLock: "off",
2426
+ timeLimitMinutes: 0,
2427
+ timeLimitWarningMinutes: 5,
2428
+ recordingConsent: "notice"
2429
+ };
2430
+ function oneOf(value, allowed, fallback) {
2431
+ return typeof value === "string" && allowed.includes(value) ? value : fallback;
2432
+ }
2433
+ function whole(value, fallback) {
2434
+ const n = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
2435
+ return Number.isFinite(n) && n >= 0 ? Math.floor(n) : fallback;
2436
+ }
2437
+ function livePolicyFrom(raw) {
2438
+ const o = typeof raw === "object" && raw !== null ? raw : {};
2439
+ return {
2440
+ chatMode: oneOf(o["chat_mode"], ["everyone", "hosts_only", "off"], DEFAULT_LIVE_POLICY.chatMode),
2441
+ avLock: oneOf(o["attendee_av_lock"], ["off", "mic", "camera", "both"], DEFAULT_LIVE_POLICY.avLock),
2442
+ timeLimitMinutes: whole(o["time_limit_minutes"], 0),
2443
+ timeLimitWarningMinutes: whole(o["time_limit_warning_minutes"], DEFAULT_LIVE_POLICY.timeLimitWarningMinutes),
2444
+ recordingConsent: oneOf(
2445
+ o["recording_consent"],
2446
+ ["notice", "ok_or_leave", "view_only_until_agree"],
2447
+ DEFAULT_LIVE_POLICY.recordingConsent
2448
+ )
2449
+ };
2450
+ }
2451
+ function samePolicy(a, b) {
2452
+ return a.chatMode === b.chatMode && a.avLock === b.avLock && a.timeLimitMinutes === b.timeLimitMinutes && a.timeLimitWarningMinutes === b.timeLimitWarningMinutes && a.recordingConsent === b.recordingConsent;
2453
+ }
2454
+ var MODERATORS = /* @__PURE__ */ new Set(["host", "cohost"]);
2455
+ function isLockedFor(policy, role, kind) {
2456
+ if (MODERATORS.has(role)) return false;
2457
+ return policy.avLock === "both" || (kind === "microphone" ? policy.avLock === "mic" : policy.avLock === "camera");
2458
+ }
2459
+ function consentPending(policy, role, recordingOn, given) {
2460
+ return recordingOn && !given && policy.recordingConsent !== "notice" && !MODERATORS.has(role);
2461
+ }
2462
+ function canChat(policy, role) {
2463
+ if (policy.chatMode === "everyone") return true;
2464
+ return policy.chatMode === "hosts_only" && MODERATORS.has(role);
2465
+ }
2466
+ function minutesLeft(policy, startedAtMs, nowMs) {
2467
+ if (policy.timeLimitMinutes <= 0 || startedAtMs === null) return null;
2468
+ return Math.ceil((startedAtMs + policy.timeLimitMinutes * 6e4 - nowMs) / 6e4);
2469
+ }
2470
+
2383
2471
  // src/core/attendance.ts
2472
+ var IDLE_PREVIEW = { state: "idle", unavailable: null };
2384
2473
  var IDLE_MEDIA_REGION = {
2385
2474
  permission: { microphone: "unknown", camera: "unknown" },
2386
2475
  audioOutput: "ok",
2387
2476
  notice: null,
2388
- offer: null
2477
+ offer: null,
2478
+ preview: { microphone: IDLE_PREVIEW, camera: IDLE_PREVIEW }
2389
2479
  };
2390
2480
  var IDLE_TRACK = { enabled: false, unavailable: null };
2391
2481
  var IDLE_MEDIA = {
@@ -3506,6 +3596,8 @@ function planFromRow(row) {
3506
3596
  minutes: num2(row["minutes"]) ?? 0,
3507
3597
  openedAt: str(row["opened_at"]),
3508
3598
  endsAt: str(row["ends_at"]),
3599
+ closingAt: str(row["closing_at"]),
3600
+ moveMode: row["move_mode"] === "auto" ? "auto" : "invite",
3509
3601
  closedAt: str(row["closed_at"]),
3510
3602
  broadcasts: (Array.isArray(row["broadcasts"]) ? row["broadcasts"] : []).map(broadcastFrom).filter((b) => b !== null)
3511
3603
  };
@@ -3657,9 +3749,17 @@ function autoAssign(identities, rooms, exclude = []) {
3657
3749
  return out;
3658
3750
  }
3659
3751
  function breakoutRemainingMs(plan, now) {
3660
- if (plan === null || plan.state !== "open" || plan.endsAt === null) return null;
3661
- const end = Date.parse(plan.endsAt);
3662
- return Number.isNaN(end) ? null : Math.max(0, end - now);
3752
+ if (plan === null || plan.state !== "open") return null;
3753
+ const ends = [plan.endsAt, plan.closingAt].map((stamp) => stamp === null ? Number.NaN : Date.parse(stamp)).filter((time) => !Number.isNaN(time));
3754
+ if (ends.length === 0) return null;
3755
+ return Math.max(0, Math.min(...ends) - now);
3756
+ }
3757
+ var BREAKOUT_COUNTDOWN_MS = 3e4;
3758
+ function breakoutClosingMs(plan, now) {
3759
+ if (plan === null || plan.state !== "open") return null;
3760
+ const remaining = breakoutRemainingMs(plan, now);
3761
+ if (remaining === null) return null;
3762
+ return plan.closingAt !== null || remaining <= BREAKOUT_COUNTDOWN_MS ? remaining : null;
3663
3763
  }
3664
3764
  function defaultCaptionLanguage(settings, viewerLanguage) {
3665
3765
  const configured = settings?.captionTranslationLanguage.trim() ?? "";
@@ -3836,6 +3936,11 @@ var EMPTY = {
3836
3936
  presenter: null,
3837
3937
  waitingCount: 0,
3838
3938
  leavePolicy: DEFAULT_LEAVE_POLICY,
3939
+ livePolicy: DEFAULT_LIVE_POLICY,
3940
+ canReclaim: false,
3941
+ recordingConsentGiven: false,
3942
+ sessionStartedAt: null,
3943
+ mutedByHost: false,
3839
3944
  moderationNotice: null,
3840
3945
  actionNotice: null,
3841
3946
  privateChatRequest: null,
@@ -3845,6 +3950,8 @@ var EMPTY = {
3845
3950
  names: {},
3846
3951
  wrapUp: null,
3847
3952
  noteTakerRefusal: null,
3953
+ captionsOn: false,
3954
+ captionsRefusal: null,
3848
3955
  media: IDLE_MEDIA,
3849
3956
  mediaRegion: IDLE_MEDIA_REGION,
3850
3957
  failure: null,
@@ -4049,7 +4156,12 @@ function createMeetStore(initial = {}) {
4049
4156
  if (current.enabled === next.enabled && current.unavailable?.code === next.unavailable?.code && current.unavailable?.reason === next.unavailable?.reason && current.unavailable?.message === next.unavailable?.message) {
4050
4157
  return;
4051
4158
  }
4052
- commit({ ...state, media: { ...state.media, [kind]: next } });
4159
+ commit({
4160
+ ...state,
4161
+ media: { ...state.media, [kind]: next },
4162
+ // The person turned their own microphone on again: the host's mute is over.
4163
+ ...kind === "microphone" && next.enabled && state.mutedByHost ? { mutedByHost: false } : {}
4164
+ });
4053
4165
  },
4054
4166
  reset() {
4055
4167
  const phase = nextPhase(state.phase, { type: "reset", teardown: true }) ?? EMPTY.phase;
@@ -4111,6 +4223,7 @@ function applyDoorRow(store, attendee) {
4111
4223
  function decidedState(state) {
4112
4224
  return WAITING_ADMISSION_STATES.includes(state) ? "expired" : state;
4113
4225
  }
4226
+ var LIVE_POLICY_POLL_MS = 3e3;
4114
4227
  function createMeetingSession(options) {
4115
4228
  const { manager, repository, store, identity } = options;
4116
4229
  let channel = null;
@@ -4252,6 +4365,46 @@ function createMeetingSession(options) {
4252
4365
  stopWaiting();
4253
4366
  }
4254
4367
  }
4368
+ let policyTimer = null;
4369
+ let policyReading = false;
4370
+ async function readLivePolicy() {
4371
+ const meeting = store.snapshot().meeting;
4372
+ const pass = options.resolvePass?.() ?? null;
4373
+ if (meeting === null || pass === null || policyReading) return;
4374
+ policyReading = true;
4375
+ try {
4376
+ const response = await options.api.request({
4377
+ path: `${MEET_ROUTES.state}?meeting_id=${encodeURIComponent(meeting.id)}`,
4378
+ method: "GET",
4379
+ requireAuth: false,
4380
+ operation: "meet.state(policy)",
4381
+ headers: { [MEETING_PASS_HEADER]: pass }
4382
+ });
4383
+ if (!response.ok) return;
4384
+ const read = await response.json();
4385
+ const next = livePolicyFrom(read.live_policy);
4386
+ const started = read.session_started_at ? Date.parse(read.session_started_at) : NaN;
4387
+ const snapshot = store.snapshot();
4388
+ const startedAt = Number.isFinite(started) ? started : snapshot.sessionStartedAt;
4389
+ if (!samePolicy(snapshot.livePolicy, next) || startedAt !== snapshot.sessionStartedAt) {
4390
+ store.patch({ livePolicy: next, sessionStartedAt: startedAt });
4391
+ }
4392
+ } catch {
4393
+ } finally {
4394
+ policyReading = false;
4395
+ }
4396
+ }
4397
+ function syncLivePolicyTimer() {
4398
+ const inCall = store.snapshot().phase === "in_call";
4399
+ if (inCall && policyTimer === null) {
4400
+ void readLivePolicy();
4401
+ policyTimer = setInterval(() => void readLivePolicy(), LIVE_POLICY_POLL_MS);
4402
+ } else if (!inCall && policyTimer !== null) {
4403
+ clearInterval(policyTimer);
4404
+ policyTimer = null;
4405
+ }
4406
+ }
4407
+ const unsubscribePolicy = store.subscribe(syncLivePolicyTimer);
4255
4408
  async function readState2() {
4256
4409
  if (reading !== null) return reading;
4257
4410
  const meeting = store.snapshot().meeting;
@@ -4638,6 +4791,9 @@ function createMeetingSession(options) {
4638
4791
  },
4639
4792
  dispose() {
4640
4793
  stopWaiting();
4794
+ unsubscribePolicy();
4795
+ if (policyTimer !== null) clearInterval(policyTimer);
4796
+ policyTimer = null;
4641
4797
  channel?.close();
4642
4798
  channel = null;
4643
4799
  meetingChannel?.close();
@@ -4676,6 +4832,47 @@ function createMeetSessionIdentity(options) {
4676
4832
  });
4677
4833
  }
4678
4834
 
4835
+ // src/core/capabilities.ts
4836
+ var IN_APP = [
4837
+ [/Instagram/i, "Instagram"],
4838
+ [/FBAN|FBAV|FB_IAB/i, "Facebook"],
4839
+ [/LinkedInApp/i, "LinkedIn"],
4840
+ [/MicroMessenger/i, "WeChat"],
4841
+ [/Snapchat/i, "Snapchat"],
4842
+ [/\bLine\//i, "LINE"],
4843
+ [/Twitter|TwitterAndroid/i, "X"],
4844
+ [/TikTok|musical_ly|BytedanceWebview/i, "TikTok"]
4845
+ ];
4846
+ function isMobileUserAgent(userAgent, maxTouchPoints = 0) {
4847
+ if (/iPhone|iPod|Android|Mobile|iPad/i.test(userAgent)) return true;
4848
+ return /Macintosh/.test(userAgent) && maxTouchPoints > 1;
4849
+ }
4850
+ function detectCapabilities(scope = typeof globalThis === "undefined" ? null : globalThis) {
4851
+ if (scope === null || scope.navigator === void 0) {
4852
+ return { canJoin: true, reason: null, inAppBrowser: null, mobile: false, canShareScreen: false };
4853
+ }
4854
+ const ua = scope.navigator.userAgent ?? "";
4855
+ const webrtc = typeof scope.RTCPeerConnection === "function" || typeof scope.webkitRTCPeerConnection === "function";
4856
+ const mobile = isMobileUserAgent(ua, scope.navigator.maxTouchPoints ?? 0);
4857
+ const inAppBrowser = IN_APP.find(([pattern]) => pattern.test(ua))?.[1] ?? null;
4858
+ return {
4859
+ canJoin: webrtc,
4860
+ reason: webrtc ? null : "no_webrtc",
4861
+ inAppBrowser,
4862
+ mobile,
4863
+ canShareScreen: !mobile && typeof scope.navigator.mediaDevices?.getDisplayMedia === "function"
4864
+ };
4865
+ }
4866
+ function unsupportedWords(capabilities) {
4867
+ if (capabilities.inAppBrowser !== null) {
4868
+ return {
4869
+ title: `${capabilities.inAppBrowser}'s browser can't join calls`,
4870
+ body: "Open this link in Chrome, Safari, Edge or Firefox"
4871
+ };
4872
+ }
4873
+ return { title: "This browser can't join calls", body: "Open this link in Chrome, Safari, Edge or Firefox" };
4874
+ }
4875
+
4679
4876
  // src/core/media-supervisor.ts
4680
4877
  function classifyDeviceFailure(error, permission) {
4681
4878
  if (permission === "denied") return "denied";
@@ -4736,7 +4933,7 @@ function deviceFixSteps(kind, reason, where) {
4736
4933
  case "safari":
4737
4934
  return where.os === "ios" ? [`Settings \u203A Apps \u203A Safari \u203A ${device}`, "Choose Allow", "Return here and press Try again"] : ["Safari \u203A Settings for This Website", `Set ${device} to Allow`, "Press Try again"];
4738
4935
  default:
4739
- return ["Click the icon left of the web address", `Set ${device} to Allow`, "Press Try again"];
4936
+ return ["Click the site settings icon in the address bar", `Set ${device} to Allow`, "Press Try again"];
4740
4937
  }
4741
4938
  case "blocked_os": {
4742
4939
  const app = where.browser === "chrome" ? "Google Chrome" : where.browser === "edge" ? "Microsoft Edge" : where.browser === "firefox" ? "Firefox" : "your browser";
@@ -4834,7 +5031,7 @@ function createMediaSupervisor(options) {
4834
5031
  function setRegion(next) {
4835
5032
  const current = region();
4836
5033
  const merged = { ...current, ...next };
4837
- if (merged.audioOutput === current.audioOutput && merged.notice === current.notice && merged.offer === current.offer && merged.permission.microphone === current.permission.microphone && merged.permission.camera === current.permission.camera) {
5034
+ if (merged.audioOutput === current.audioOutput && merged.notice === current.notice && merged.offer === current.offer && merged.preview === current.preview && merged.permission.microphone === current.permission.microphone && merged.permission.camera === current.permission.camera) {
4838
5035
  return;
4839
5036
  }
4840
5037
  store.patch({ mediaRegion: merged });
@@ -4930,7 +5127,7 @@ function createMediaSupervisor(options) {
4930
5127
  if (permission !== "unknown") setPermission(kind, permission);
4931
5128
  return unavailableFrom(normalized, classifyDeviceFailure(normalized, permission));
4932
5129
  }
4933
- return {
5130
+ const api = {
4934
5131
  start() {
4935
5132
  if (started || platform === null) return;
4936
5133
  started = true;
@@ -4952,6 +5149,16 @@ function createMediaSupervisor(options) {
4952
5149
  started = false;
4953
5150
  },
4954
5151
  unavailable,
5152
+ async preview(kind, outcome) {
5153
+ api.start();
5154
+ let next;
5155
+ if (typeof outcome === "string") next = { state: outcome, unavailable: null };
5156
+ else next = { state: "unavailable", unavailable: await unavailable(outcome.error, kind, `meet.${kind}Preview`) };
5157
+ const current = region().preview;
5158
+ const was = current[kind];
5159
+ if (was.state === next.state && was.unavailable?.reason === next.unavailable?.reason && was.unavailable?.message === next.unavailable?.message) return;
5160
+ setRegion({ preview: { ...region().preview, [kind]: next } });
5161
+ },
4955
5162
  onAudioPlayback(canPlay) {
4956
5163
  setRegion({ audioOutput: canPlay ? "ok" : "blocked" });
4957
5164
  },
@@ -5010,6 +5217,7 @@ function createMediaSupervisor(options) {
5010
5217
  setRegion({ notice: null });
5011
5218
  }
5012
5219
  };
5220
+ return api;
5013
5221
  }
5014
5222
 
5015
5223
  // src/core/room-events.ts
@@ -5229,10 +5437,11 @@ function moderationText(kind, payload) {
5229
5437
  switch (kind) {
5230
5438
  case "muted": {
5231
5439
  const source = text2(payload["source"]) ?? "microphone";
5232
- return source === "microphone" ? `${by} muted you.` : source === "camera" ? `${by} turned off your camera.` : `${by} stopped your screen share.`;
5440
+ const who = payload["by_role"] === "host" || payload["by_role"] === "cohost" ? "the host" : by;
5441
+ 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}.`;
5233
5442
  }
5234
5443
  case "ask-unmute":
5235
- return `${by} is asking you to unmute.`;
5444
+ return `${by === "The host" ? "The host" : by} asked you to unmute.`;
5236
5445
  case "removed":
5237
5446
  return `${by} removed you from this meeting.`;
5238
5447
  default:
@@ -5258,7 +5467,7 @@ function applyModeration(store, payload, now) {
5258
5467
  grants: { ...snapshot.grants, role },
5259
5468
  moderationNotice: {
5260
5469
  kind: "role",
5261
- text: role === "host" ? "You are now the host." : role === "cohost" ? "You are now a co-host." : "You are no longer a co-host.",
5470
+ 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.",
5262
5471
  at: now()
5263
5472
  }
5264
5473
  });
@@ -5270,7 +5479,10 @@ function applyModeration(store, payload, now) {
5270
5479
  case "removed": {
5271
5480
  const sentence = moderationText(kind, payload);
5272
5481
  if (sentence === null) return ignored("a moderation event with no sentence");
5273
- store.patch({ moderationNotice: { kind, text: sentence, at: now() } });
5482
+ store.patch({
5483
+ moderationNotice: { kind, text: sentence, at: now() },
5484
+ ...kind === "muted" && (text2(payload["source"]) ?? "microphone") === "microphone" ? { mutedByHost: true } : {}
5485
+ });
5274
5486
  return HANDLED;
5275
5487
  }
5276
5488
  default:
@@ -5309,7 +5521,7 @@ function exactNoteTakerParticipant(meeting, participants) {
5309
5521
  }
5310
5522
  function noteTakerStatus(args) {
5311
5523
  const present = exactNoteTakerParticipant(args.meeting, args.participants) !== null;
5312
- const raw = args.meeting?.metadata[NOTE_TAKER_METADATA_KEY];
5524
+ const raw = args.meeting?.metadata?.[NOTE_TAKER_METADATA_KEY];
5313
5525
  const row = typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : {};
5314
5526
  const recorded = readState(row["state"]);
5315
5527
  const detail = typeof row["reason"] === "string" && row["reason"].length > 0 ? row["reason"] : null;
@@ -5517,7 +5729,8 @@ function createRoomEngine(options) {
5517
5729
  message: normalized.message,
5518
5730
  remedy: normalized.remedy,
5519
5731
  reason: normalized.reason,
5520
- retryAt: normalized.retryAfterSeconds === null ? null : now() + normalized.retryAfterSeconds * 1e3
5732
+ retryAt: normalized.retryAfterSeconds === null ? null : now() + normalized.retryAfterSeconds * 1e3,
5733
+ stage: "join"
5521
5734
  }
5522
5735
  });
5523
5736
  store.dispatch({ type: "join_failed", reason: normalized.reason ?? null });
@@ -5786,6 +5999,7 @@ function createRoomEngine(options) {
5786
5999
  lastServerUrl = token.serverUrl;
5787
6000
  connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;
5788
6001
  if (token.leavePolicy !== void 0) store.patch({ leavePolicy: token.leavePolicy });
6002
+ store.patch({ canReclaim: token.canReclaim === true });
5789
6003
  store.patch({
5790
6004
  localIdentity: token.identity,
5791
6005
  breakoutRoomId: token.breakoutRoomId ?? null,
@@ -5907,6 +6121,10 @@ function createRoomEngine(options) {
5907
6121
  }
5908
6122
  departures.clear();
5909
6123
  liveRoster = list;
6124
+ const localNow = list.find((p) => p.isLocal);
6125
+ if (localNow !== void 0 && !localNow.screenSharing && store.snapshot().share.state === "presenting") {
6126
+ store.patch({ share: NO_SHARE });
6127
+ }
5910
6128
  const merged = mergeGhosts(list, ghosts, at);
5911
6129
  ghosts = merged.kept;
5912
6130
  const people = /* @__PURE__ */ new Map();
@@ -6126,7 +6344,10 @@ function createRoomEngine(options) {
6126
6344
  try {
6127
6345
  await driver.setScreenShareEnabled(enabled);
6128
6346
  ambiguousShareDenials = 0;
6129
- store.patch({ share: { state: enabled ? "presenting" : "none", reason: null, remedy: null, at: now() } });
6347
+ const audio = enabled ? driver?.screenShareHasAudio?.() : void 0;
6348
+ store.patch({
6349
+ share: { state: enabled ? "presenting" : "none", reason: null, remedy: null, at: now(), ...audio !== void 0 ? { audio } : {} }
6350
+ });
6130
6351
  } catch (error) {
6131
6352
  const kind = classifyScreenShareError(error);
6132
6353
  if (kind === "ambiguous") {
@@ -6193,13 +6414,14 @@ function createRoomEngine(options) {
6193
6414
  return driver?.supportsVideoEffects?.() ?? false;
6194
6415
  },
6195
6416
  supportsScreenShare() {
6196
- return driver?.supportsScreenShare?.() ?? true;
6417
+ return driver?.supportsScreenShare?.() ?? detectCapabilities().canShareScreen;
6197
6418
  },
6198
6419
  async switchDevice(kind, deviceId2) {
6199
6420
  if (driver === null) return;
6200
6421
  await driver.switchDevice(kind, deviceId2);
6201
6422
  },
6202
6423
  enableAudio: () => media.enableAudio(),
6424
+ previewDevice: (kind, outcome) => media.preview(kind, outcome),
6203
6425
  acceptDeviceOffer: () => media.acceptOffer(),
6204
6426
  dismissDeviceOffer: () => media.dismissOffer(),
6205
6427
  dismissDeviceNotice: () => media.dismissNotice(),
@@ -6285,8 +6507,12 @@ var MODERATION_ACTIONS = [
6285
6507
  "lower_hand",
6286
6508
  "lower_all_hands",
6287
6509
  "make_cohost",
6510
+ "make_presenter",
6511
+ "revoke_presenter",
6512
+ "set_av_lock",
6288
6513
  "revoke_cohost",
6289
6514
  "transfer_host",
6515
+ "reclaim_host",
6290
6516
  "rename",
6291
6517
  "remove",
6292
6518
  "report"
@@ -6667,7 +6893,15 @@ function createLiveKitDriver(handlers, options = {}) {
6667
6893
  created.on(RoomEvent.ParticipantConnected, roster).on(RoomEvent.ParticipantDisconnected, (participant, reason) => {
6668
6894
  handlers.participantLeft?.(participant.identity, reasonName(reason));
6669
6895
  roster();
6670
- }).on(RoomEvent.TrackSubscribed, roster).on(RoomEvent.TrackUnsubscribed, roster).on(RoomEvent.TrackMuted, roster).on(RoomEvent.TrackUnmuted, roster).on(RoomEvent.LocalTrackPublished, roster).on(RoomEvent.LocalTrackUnpublished, roster).on(RoomEvent.ParticipantNameChanged, roster).on(RoomEvent.ParticipantAttributesChanged, roster).on(RoomEvent.ParticipantMetadataChanged, roster).on(RoomEvent.RoomMetadataChanged, (metadata) => {
6896
+ }).on(RoomEvent.TrackSubscribed, roster).on(RoomEvent.TrackUnsubscribed, roster).on(RoomEvent.TrackMuted, roster).on(RoomEvent.TrackUnmuted, roster).on(RoomEvent.LocalTrackPublished, (publication) => {
6897
+ if (publication.source === Track.Source.ScreenShare) {
6898
+ const ended = () => {
6899
+ void created.localParticipant.setScreenShareEnabled(false).catch(() => void 0);
6900
+ };
6901
+ publication.track?.mediaStreamTrack.addEventListener("ended", ended, { once: true });
6902
+ }
6903
+ roster();
6904
+ }).on(RoomEvent.LocalTrackUnpublished, roster).on(RoomEvent.ParticipantNameChanged, roster).on(RoomEvent.ParticipantAttributesChanged, roster).on(RoomEvent.ParticipantMetadataChanged, roster).on(RoomEvent.RoomMetadataChanged, (metadata) => {
6671
6905
  handlers.roomMetadata?.(metadata ?? "");
6672
6906
  }).on(RoomEvent.AudioPlaybackStatusChanged, (canPlay) => {
6673
6907
  handlers.audioPlayback?.(canPlay);
@@ -6808,7 +7042,10 @@ function createLiveKitDriver(handlers, options = {}) {
6808
7042
  },
6809
7043
  async setScreenShareEnabled(enabled) {
6810
7044
  try {
6811
- await requireRoom("setScreenShareEnabled").localParticipant.setScreenShareEnabled(enabled);
7045
+ await requireRoom("setScreenShareEnabled").localParticipant.setScreenShareEnabled(
7046
+ enabled,
7047
+ enabled ? { audio: true, systemAudio: "include" } : void 0
7048
+ );
6812
7049
  } catch (error) {
6813
7050
  throw normalizeScreenShareError(error, "meet.setScreenShareEnabled");
6814
7051
  }
@@ -6847,8 +7084,13 @@ function createLiveKitDriver(handlers, options = {}) {
6847
7084
  supportsVideoEffects() {
6848
7085
  return videoEffectsSupported();
6849
7086
  },
7087
+ screenShareHasAudio() {
7088
+ const target = room;
7089
+ if (target === null) return false;
7090
+ return target.localParticipant.getTrackPublications().some((p) => String(p.source) === "screen_share_audio" && p.track !== void 0);
7091
+ },
6850
7092
  supportsScreenShare() {
6851
- return typeof navigator !== "undefined" && typeof navigator.mediaDevices?.getDisplayMedia === "function";
7093
+ return detectCapabilities().canShareScreen;
6852
7094
  },
6853
7095
  attach(identity, kind, element) {
6854
7096
  const target = requireRoom("attach");
@@ -7320,18 +7562,35 @@ function useLocalUserId() {
7320
7562
  }
7321
7563
 
7322
7564
  // src/react/root.tsx
7323
- import { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2 } from "react";
7565
+ import { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
7324
7566
 
7325
7567
  // src/core/observation.ts
7326
7568
  function observedRole(role, standing) {
7327
- if (role === "host" || role === "cohost") return role;
7569
+ if (role === "host" || role === "cohost" || role === "presenter") return role;
7328
7570
  return isLinkFollower(standing) ? "guest" : "participant";
7329
7571
  }
7330
- function observedDevice(track, permission = "unknown") {
7572
+ function observedDevice(track, permission = "unknown", held = null) {
7573
+ if (held === "locked") return "locked";
7331
7574
  if (track.enabled) return "on";
7575
+ if (held === "muted_by_host") return "muted_by_host";
7332
7576
  const reason = deviceUnavailableReason(track, permission);
7333
7577
  return reason === null ? "muted_by_me" : `unavailable:${reason}`;
7334
7578
  }
7579
+ function observedPreview(preview, permission = "unknown") {
7580
+ if (permission === "denied") return "unavailable:denied";
7581
+ switch (preview.state) {
7582
+ case "on":
7583
+ return "on";
7584
+ case "off":
7585
+ return "muted_by_me";
7586
+ case "prompting":
7587
+ return "prompting";
7588
+ case "unavailable":
7589
+ return `unavailable:${preview.unavailable?.reason ?? "failed"}`;
7590
+ default:
7591
+ return null;
7592
+ }
7593
+ }
7335
7594
  function endedRecord(snapshot, given) {
7336
7595
  const record = snapshot?.meeting ?? given;
7337
7596
  return record !== null && record.endedAt !== null;
@@ -7355,20 +7614,34 @@ function observe(input) {
7355
7614
  const canAdmit = actual !== "none" && ADMITTER_ROLES.has(actual);
7356
7615
  const role = actual === "none" || snapshot === null ? "none" : observedRole(actual, effectiveStanding(snapshot));
7357
7616
  const deviceInPlay = phase === "joining" || inCall;
7617
+ const region = snapshot?.mediaRegion;
7618
+ const atPrejoin = phase === "prejoin" && region !== void 0;
7358
7619
  return {
7359
7620
  phase,
7360
7621
  reason: phase === "ended" ? snapshot?.reason ?? null : snapshot?.reason ?? reasonOf(phase),
7361
7622
  role,
7362
- connection: snapshot?.connection ?? "stable",
7623
+ // `net-blip`: in the call, a drop shorter than `reconnect_banner_delay_ms` is not something the
7624
+ // person sees — the contract says what is on screen, so it reads `stable` until the banner shows.
7625
+ connection: snapshot === null ? "stable" : inCall && snapshot.connection !== "stable" && !snapshot.connectionBanner ? "stable" : snapshot.connection,
7363
7626
  hostPresent: inCall && snapshot !== null ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role)) : snapshot !== null && isWaitingPhase(snapshot.phase) ? snapshot.waiting?.admittersPresent ?? null : null,
7364
7627
  lobbyCount: canAdmit && snapshot !== null ? snapshot.lobby.filter((entry) => entry.state === "knocking").length : 0,
7365
7628
  recording: snapshot?.recording === "recording" || snapshot?.recording === "stopping",
7366
7629
  // S6c: the autoplay region is known only inside the room (no remote audio exists elsewhere).
7367
7630
  audioBlocked: inCall && snapshot !== null ? snapshot.mediaRegion.audioOutput === "blocked" : null,
7368
- camera: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.camera, snapshot.mediaRegion.permission.camera) : null,
7369
- microphone: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.microphone, snapshot.mediaRegion.permission.microphone) : null
7631
+ camera: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.camera, snapshot.mediaRegion.permission.camera, lockedFor(snapshot, "camera", inCall)) : atPrejoin ? observedPreview(region.preview.camera, region.permission.camera) : null,
7632
+ microphone: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.microphone, snapshot.mediaRegion.permission.microphone, lockedFor(snapshot, "microphone", inCall)) : atPrejoin ? observedPreview(region.preview.microphone, region.permission.microphone) : null
7370
7633
  };
7371
7634
  }
7635
+ function lockedFor(snapshot, kind, inCall) {
7636
+ if (!inCall) return null;
7637
+ const role = effectiveRole(snapshot);
7638
+ if (isLockedFor(snapshot.livePolicy, role, kind)) return "locked";
7639
+ if (snapshot.livePolicy.recordingConsent === "view_only_until_agree" && consentPending(snapshot.livePolicy, role, snapshot.recording === "recording", snapshot.recordingConsentGiven)) {
7640
+ return "locked";
7641
+ }
7642
+ if (kind === "microphone" && snapshot.mutedByHost) return "muted_by_host";
7643
+ return null;
7644
+ }
7372
7645
  function staticObservation(phase, reason = null) {
7373
7646
  return {
7374
7647
  phase,
@@ -7438,7 +7711,7 @@ function microphoneNoticeCode(track, permission = "unknown") {
7438
7711
  }
7439
7712
 
7440
7713
  // src/react/root.tsx
7441
- import { jsx as jsx2 } from "react/jsx-runtime";
7714
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
7442
7715
  function useCoreObservation(options = {}) {
7443
7716
  const snapshot = useMeetSnapshot();
7444
7717
  const { meeting, phase, reason } = options;
@@ -7456,7 +7729,13 @@ function MeetRoot({
7456
7729
  ...options
7457
7730
  }) {
7458
7731
  const snapshot = useMeetSnapshot();
7459
- const observation = useCoreObservation(options);
7732
+ const probed = useCapabilities();
7733
+ const base = useCoreObservation(options);
7734
+ const unsupported = probed !== null && !probed.canJoin;
7735
+ const observation = useMemo2(
7736
+ () => unsupported ? { ...staticObservation("unsupported", probed?.reason ?? null) } : base,
7737
+ [unsupported, probed, base]
7738
+ );
7460
7739
  const attrs = useMemo2(() => rootAttributes(observation), [observation]);
7461
7740
  const latest = useRef2({ observation, storePhase: snapshot?.phase ?? null });
7462
7741
  latest.current = { observation, storePhase: snapshot?.phase ?? null };
@@ -7474,11 +7753,38 @@ function MeetRoot({
7474
7753
  if (target.__matrxMeet === diagnostics) delete target.__matrxMeet;
7475
7754
  };
7476
7755
  }, []);
7477
- return /* @__PURE__ */ jsx2("div", { ...attrs, style: { display: "contents" }, children });
7756
+ return /* @__PURE__ */ jsx2("div", { ...attrs, style: { display: "contents" }, children: unsupported && probed !== null ? /* @__PURE__ */ jsx2(UnsupportedBrowser, { capabilities: probed }) : children });
7757
+ }
7758
+ function useCapabilities() {
7759
+ const [found, setFound] = useState2(null);
7760
+ useEffect2(() => {
7761
+ setFound(detectCapabilities());
7762
+ }, []);
7763
+ return found;
7764
+ }
7765
+ function UnsupportedBrowser({ capabilities }) {
7766
+ const words = unsupportedWords(capabilities);
7767
+ const [copied, setCopied] = useState2(false);
7768
+ const href = typeof window === "undefined" ? "" : window.location.href;
7769
+ return /* @__PURE__ */ jsx2("div", { className: "mx-meet", children: /* @__PURE__ */ jsxs("div", { className: "mx-meet__waiting", "data-meet-exit": "unsupported", children: [
7770
+ /* @__PURE__ */ jsx2("h1", { className: "mx-meet__prejoin-title", children: words.title }),
7771
+ /* @__PURE__ */ jsx2("p", { className: "mx-meet__hint", role: "status", "data-meet-notice": "browser-unsupported", children: words.body }),
7772
+ href.length > 0 ? /* @__PURE__ */ jsx2(
7773
+ "button",
7774
+ {
7775
+ type: "button",
7776
+ className: "mx-meet__primary",
7777
+ onClick: () => {
7778
+ void navigator.clipboard?.writeText(href).then(() => setCopied(true), () => void 0);
7779
+ },
7780
+ children: copied ? "Link copied" : "Copy link"
7781
+ }
7782
+ ) : null
7783
+ ] }) });
7478
7784
  }
7479
7785
 
7480
7786
  // src/react/hooks.ts
7481
- import { useCallback as useCallback2, useEffect as useEffect3, useMemo as useMemo3, useRef as useRef3, useState as useState2, useSyncExternalStore as useSyncExternalStore2 } from "react";
7787
+ import { useCallback as useCallback2, useEffect as useEffect3, useMemo as useMemo3, useRef as useRef3, useState as useState3, useSyncExternalStore as useSyncExternalStore2 } from "react";
7482
7788
  import { useRealtimeManager as useRealtimeManager2 } from "@ai-matrx/realtime/react";
7483
7789
 
7484
7790
  // src/core/record.ts
@@ -8174,7 +8480,8 @@ function useRoom() {
8174
8480
  failure: snapshot?.failure ?? null,
8175
8481
  secondDevice: snapshot?.secondDevice ?? null,
8176
8482
  local,
8177
- canShareScreen: engine?.supportsScreenShare() ?? true,
8483
+ // An observer (role `viewer`) publishes nothing: no Share screen to press and be refused (MD-16).
8484
+ canShareScreen: (engine?.supportsScreenShare() ?? true) && !isObserverRole(snapshot?.grants.role ?? null),
8178
8485
  join: useCallback2(
8179
8486
  async (request) => requireEngine(engine, "join").join(request),
8180
8487
  [engine]
@@ -8231,7 +8538,7 @@ function useRoom() {
8231
8538
  }
8232
8539
  function useStage(pageSize = DEFAULT_PAGE_SIZE) {
8233
8540
  const snapshot = useMeetSnapshot();
8234
- const [page, setPage] = useState2(0);
8541
+ const [page, setPage] = useState3(0);
8235
8542
  const stage = useMemo3(
8236
8543
  () => computeStage({
8237
8544
  participants: snapshot?.participants ?? [],
@@ -8265,7 +8572,7 @@ function useTrack(identity, kind) {
8265
8572
  function useCalls() {
8266
8573
  const host = useMeetHost();
8267
8574
  const center = host?.calls ?? null;
8268
- const [, force] = useState2(0);
8575
+ const [, force] = useState3(0);
8269
8576
  useEffect3(() => {
8270
8577
  if (center === null) return void 0;
8271
8578
  const timer = setInterval(() => force((value) => value + 1), 1e3);
@@ -8345,7 +8652,7 @@ function useLobby() {
8345
8652
  };
8346
8653
  }
8347
8654
  function useCountdown(untilMs) {
8348
- const [now, setNow] = useState2(() => Date.now());
8655
+ const [now, setNow] = useState3(() => Date.now());
8349
8656
  const active = typeof untilMs === "number" && untilMs > now;
8350
8657
  useEffect3(() => {
8351
8658
  if (!active) return void 0;
@@ -8358,7 +8665,7 @@ function useCountdown(untilMs) {
8358
8665
  }
8359
8666
  function useKnockNotice() {
8360
8667
  const lobby = useLobby();
8361
- const [dismissed, setDismissed] = useState2([]);
8668
+ const [dismissed, setDismissed] = useState3([]);
8362
8669
  const keyOf = (entry) => `${entry.identity}@${entry.knockedAt}`;
8363
8670
  const open = lobby.canAdmit ? lobby.waiting.filter((entry) => !dismissed.includes(keyOf(entry))) : [];
8364
8671
  const knocker = open[0] ?? null;
@@ -8444,11 +8751,11 @@ function useMeetAi() {
8444
8751
  const host = useMeetHost();
8445
8752
  const snapshot = useMeetSnapshot();
8446
8753
  const isHost = useIsHost();
8447
- const [asking, setAsking] = useState2(false);
8448
- const [answer, setAnswer] = useState2(null);
8449
- const [coverage, setCoverage] = useState2(null);
8450
- const [phase, setPhase] = useState2(null);
8451
- const [error, setError] = useState2(null);
8754
+ const [asking, setAsking] = useState3(false);
8755
+ const [answer, setAnswer] = useState3(null);
8756
+ const [coverage, setCoverage] = useState3(null);
8757
+ const [phase, setPhase] = useState3(null);
8758
+ const [error, setError] = useState3(null);
8452
8759
  const ai = host?.ai ?? null;
8453
8760
  const meeting = snapshot?.meeting ?? null;
8454
8761
  const participants = snapshot?.participants ?? [];
@@ -8558,12 +8865,51 @@ function useMeetAi() {
8558
8865
  }, [ai, meeting, dispatchNoteTaker])
8559
8866
  };
8560
8867
  }
8868
+ function useCaptions() {
8869
+ const host = useMeetHost();
8870
+ const snapshot = useMeetSnapshot();
8871
+ const ai = useMeetAi();
8872
+ const on = snapshot?.captionsOn === true;
8873
+ const refusal2 = snapshot?.captionsRefusal ?? null;
8874
+ const taker = ai.noteTaker;
8875
+ const meeting = snapshot?.meeting ?? null;
8876
+ const request = useCallback2(() => {
8877
+ if (host === null || meeting === null) return;
8878
+ void host.ai.requestCaptions({ meetingId: meeting.id });
8879
+ }, [host, meeting]);
8880
+ const toggle = useCallback2(() => {
8881
+ if (host === null) return;
8882
+ const next = !on;
8883
+ host.store.patch({ captionsOn: next, captionsRefusal: null });
8884
+ if (next && taker.state !== "joining" && taker.state !== "transcribing") request();
8885
+ }, [host, on, taker.state, request]);
8886
+ let state = "off";
8887
+ let reason = null;
8888
+ let remedy = null;
8889
+ if (on) {
8890
+ if (taker.state === "transcribing" && taker.present) {
8891
+ state = "live";
8892
+ } else if (taker.state === "joining") {
8893
+ state = "starting";
8894
+ } else if (refusal2 !== null && refusal2.reason.length > 0) {
8895
+ state = "unavailable";
8896
+ reason = refusal2.reason;
8897
+ remedy = refusal2.remedy.length > 0 ? refusal2.remedy : null;
8898
+ } else if (taker.state === "ended" || taker.state === "handoff_pending") {
8899
+ state = "unavailable";
8900
+ reason = noteTakerLabel(taker);
8901
+ } else {
8902
+ state = "starting";
8903
+ }
8904
+ }
8905
+ return { on, state, reason, remedy, toggle };
8906
+ }
8561
8907
  function useEndMeeting() {
8562
8908
  const host = useMeetHost();
8563
8909
  const snapshot = useMeetSnapshot();
8564
8910
  const isHost = useIsHost();
8565
- const [ending, setEnding] = useState2(false);
8566
- const [notice, setNotice] = useState2(null);
8911
+ const [ending, setEnding] = useState3(false);
8912
+ const [notice, setNotice] = useState3(null);
8567
8913
  const meeting = snapshot?.meeting ?? null;
8568
8914
  const session = host?.meetings ?? null;
8569
8915
  return {
@@ -8595,7 +8941,7 @@ function useConsentNotice() {
8595
8941
  const snapshot = useMeetSnapshot();
8596
8942
  const agent = noteTakerParticipant(snapshot?.participants ?? []);
8597
8943
  if (agent === null) return null;
8598
- return `${agent.displayName} is in this meeting: it is transcribing what is said and writing notes everyone here can read.`;
8944
+ return `AI notes are on. ${agent.displayName} is in this meeting, transcribing what is said and writing notes everyone here can read.`;
8599
8945
  }
8600
8946
  function useMediaSupervisor() {
8601
8947
  const host = useMeetHost();
@@ -8614,6 +8960,12 @@ function useMediaSupervisor() {
8614
8960
  retry: useCallback2(
8615
8961
  async (kind) => kind === "microphone" ? engine?.setMicrophoneEnabled(true) : engine?.setCameraEnabled(true),
8616
8962
  [engine]
8963
+ ),
8964
+ reportPreview: useCallback2(
8965
+ (kind, outcome) => {
8966
+ void engine?.previewDevice(kind, outcome);
8967
+ },
8968
+ [engine]
8617
8969
  )
8618
8970
  };
8619
8971
  }
@@ -8636,7 +8988,7 @@ function useReactions() {
8636
8988
  return snapshot?.reactions ?? [];
8637
8989
  }
8638
8990
  function useElapsed(startedAt) {
8639
- const [now, setNow] = useState2(() => Date.now());
8991
+ const [now, setNow] = useState3(() => Date.now());
8640
8992
  useEffect3(() => {
8641
8993
  if (startedAt === null) return void 0;
8642
8994
  const timer = setInterval(() => setNow(Date.now()), 1e3);
@@ -8661,7 +9013,7 @@ function useHostControls() {
8661
9013
  (participant) => participant.identity === snapshot.localIdentity
8662
9014
  );
8663
9015
  const actor = me === void 0 ? void 0 : { ...me, role };
8664
- const [busy, setBusy] = useState2(false);
9016
+ const [busy, setBusy] = useState3(false);
8665
9017
  const inRoom = host?.inRoom ?? null;
8666
9018
  const store = host?.store ?? null;
8667
9019
  const notice = snapshot?.actionNotice ?? null;
@@ -8744,7 +9096,7 @@ function useModerationNotice() {
8744
9096
  };
8745
9097
  }
8746
9098
  function useAudioLevel(stream) {
8747
- const [level, setLevel] = useState2(0);
9099
+ const [level, setLevel] = useState3(0);
8748
9100
  useEffect3(() => {
8749
9101
  if (stream === null || stream.getAudioTracks().length === 0) {
8750
9102
  setLevel(0);
@@ -8833,10 +9185,10 @@ function useMeetingRecord(meeting) {
8833
9185
  const repository = host?.repository ?? null;
8834
9186
  const ai = host?.ai ?? null;
8835
9187
  const readerId = host?.identity.userId ?? null;
8836
- const [asking, setAsking] = useState2(false);
8837
- const [answer, setAnswer] = useState2(null);
8838
- const [coverage, setCoverage] = useState2(null);
8839
- const [askError, setAskError] = useState2(null);
9188
+ const [asking, setAsking] = useState3(false);
9189
+ const [answer, setAnswer] = useState3(null);
9190
+ const [coverage, setCoverage] = useState3(null);
9191
+ const [askError, setAskError] = useState3(null);
8840
9192
  const meetingId = meeting?.id ?? null;
8841
9193
  const api = host?.api ?? null;
8842
9194
  const tokens = host?.tokens ?? null;
@@ -8981,11 +9333,11 @@ function useMeetings(limit = 25, organizationFilter = null) {
8981
9333
  const host = useMeetHost();
8982
9334
  const repository = host?.repository ?? null;
8983
9335
  const organizationId = organizationFilter;
8984
- const [meetings, setMeetings] = useState2([]);
8985
- const [hasMore, setHasMore] = useState2(false);
8986
- const [loading, setLoading] = useState2(true);
8987
- const [failure, setFailure] = useState2(null);
8988
- const [nonce, setNonce] = useState2(0);
9336
+ const [meetings, setMeetings] = useState3([]);
9337
+ const [hasMore, setHasMore] = useState3(false);
9338
+ const [loading, setLoading] = useState3(true);
9339
+ const [failure, setFailure] = useState3(null);
9340
+ const [nonce, setNonce] = useState3(0);
8989
9341
  useEffect3(() => {
8990
9342
  if (repository === null) return void 0;
8991
9343
  let live = true;
@@ -9020,10 +9372,10 @@ function useGuestRecordAccess(meeting) {
9020
9372
  const host = useMeetHost();
9021
9373
  const api = host?.api ?? null;
9022
9374
  const stored = guestRecordAccessSetting(meeting);
9023
- const [chosen, setChosen] = useState2(null);
9024
- const [effective, setEffective] = useState2(null);
9025
- const [saving, setSaving] = useState2(false);
9026
- const [error, setError] = useState2(null);
9375
+ const [chosen, setChosen] = useState3(null);
9376
+ const [effective, setEffective] = useState3(null);
9377
+ const [saving, setSaving] = useState3(false);
9378
+ const [error, setError] = useState3(null);
9027
9379
  useEffect3(() => {
9028
9380
  setChosen(null);
9029
9381
  }, [stored, meeting?.id]);
@@ -9059,12 +9411,44 @@ function useGuestRecordAccess(meeting) {
9059
9411
  )
9060
9412
  };
9061
9413
  }
9414
+ function useLivePolicy() {
9415
+ const snapshot = useMeetSnapshot();
9416
+ const role = useRole();
9417
+ const [now, setNow] = useState3(() => Date.now());
9418
+ const limited = (snapshot?.livePolicy.timeLimitMinutes ?? 0) > 0;
9419
+ useEffect3(() => {
9420
+ if (!limited) return void 0;
9421
+ const timer = setInterval(() => setNow(Date.now()), 1e3);
9422
+ return () => clearInterval(timer);
9423
+ }, [limited]);
9424
+ const policy = snapshot?.livePolicy ?? DEFAULT_LIVE_POLICY;
9425
+ const left = minutesLeft(policy, snapshot?.sessionStartedAt ?? null, now);
9426
+ const host = useMeetHost();
9427
+ const pending = consentPending(policy, role, snapshot?.recording === "recording", snapshot?.recordingConsentGiven === true);
9428
+ const viewOnly = pending && policy.recordingConsent === "view_only_until_agree";
9429
+ const idle = snapshot !== null && snapshot.recording !== "recording" && snapshot.recording !== "starting" && snapshot.recording !== "stopping";
9430
+ const given = snapshot?.recordingConsentGiven === true;
9431
+ const store = host?.store ?? null;
9432
+ useEffect3(() => {
9433
+ if (idle && given) store?.patch({ recordingConsentGiven: false });
9434
+ }, [idle, given, store]);
9435
+ return {
9436
+ canChat: canChat(policy, role),
9437
+ chatMode: policy.chatMode,
9438
+ micLocked: isLockedFor(policy, role, "microphone") || viewOnly,
9439
+ cameraLocked: isLockedFor(policy, role, "camera") || viewOnly,
9440
+ consent: pending ? policy.recordingConsent === "ok_or_leave" ? "ok_or_leave" : "view_only_until_agree" : null,
9441
+ agree: () => store?.patch({ recordingConsentGiven: true }),
9442
+ minutesLeft: left,
9443
+ timeWarning: left !== null && left <= policy.timeLimitWarningMinutes
9444
+ };
9445
+ }
9062
9446
 
9063
9447
  // src/react/skin-root.tsx
9064
9448
  import { useEffect as useEffect8 } from "react";
9065
9449
 
9066
9450
  // src/skins/meet/components.tsx
9067
- import { useEffect as useEffect6, useLayoutEffect, useRef as useRef6, useState as useState4 } from "react";
9451
+ import { useEffect as useEffect6, useLayoutEffect, useRef as useRef6, useState as useState5 } from "react";
9068
9452
 
9069
9453
  // src/react/facade.ts
9070
9454
  import { useMemo as useMemo5 } from "react";
@@ -9677,6 +10061,39 @@ function useCollabPanelRequest(onRequest) {
9677
10061
  // src/core/reactions.ts
9678
10062
  var MEET_REACTIONS = ["\u{1F496}", "\u{1F44D}", "\u{1F389}", "\u{1F44F}", "\u{1F602}", "\u{1F62E}", "\u{1F622}", "\u{1F914}", "\u{1F44E}"];
9679
10063
 
10064
+ // src/core/speaking-detector.ts
10065
+ function createSpeakingWhileMutedDetector(options = {}) {
10066
+ const threshold = options.threshold ?? 0.06;
10067
+ const holdMs = options.holdMs ?? 600;
10068
+ const gapMs = options.gapMs ?? 400;
10069
+ const cooldownMs = options.cooldownMs ?? 2e4;
10070
+ let speechStart = null;
10071
+ let lastLoud = null;
10072
+ let firedAt = null;
10073
+ return {
10074
+ feed(level, at) {
10075
+ if (level >= threshold) {
10076
+ if (speechStart === null || lastLoud === null || at - lastLoud > gapMs) speechStart = at;
10077
+ lastLoud = at;
10078
+ } else if (lastLoud !== null && at - lastLoud > gapMs) {
10079
+ speechStart = null;
10080
+ lastLoud = null;
10081
+ }
10082
+ if (speechStart === null || at - speechStart < holdMs) return false;
10083
+ if (firedAt !== null && at - firedAt < cooldownMs) return false;
10084
+ firedAt = at;
10085
+ speechStart = null;
10086
+ lastLoud = null;
10087
+ return true;
10088
+ },
10089
+ reset() {
10090
+ speechStart = null;
10091
+ lastLoud = null;
10092
+ firedAt = null;
10093
+ }
10094
+ };
10095
+ }
10096
+
9680
10097
  // src/react/facade.ts
9681
10098
  var NO_ACTIONS = {
9682
10099
  join: async () => void 0,
@@ -9741,10 +10158,10 @@ function useMeetCore() {
9741
10158
  import { Alert, AlertDescription, Button, Checkbox, ErrorBox, Label, SegmentedControl, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ai-matrx/design-system";
9742
10159
 
9743
10160
  // src/skins/meet/collab.tsx
9744
- import { useEffect as useEffect5, useRef as useRef5, useState as useState3 } from "react";
10161
+ import { useEffect as useEffect5, useRef as useRef5, useState as useState4 } from "react";
9745
10162
 
9746
10163
  // src/skins/meet/icons.tsx
9747
- import { jsx as jsx4, jsxs } from "react/jsx-runtime";
10164
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
9748
10165
  function Icon({ children, ...props }) {
9749
10166
  return /* @__PURE__ */ jsx4(
9750
10167
  "svg",
@@ -9765,14 +10182,14 @@ function Icon({ children, ...props }) {
9765
10182
  );
9766
10183
  }
9767
10184
  function MicIcon(props) {
9768
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10185
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9769
10186
  /* @__PURE__ */ jsx4("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }),
9770
10187
  /* @__PURE__ */ jsx4("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
9771
10188
  /* @__PURE__ */ jsx4("path", { d: "M12 19v3" })
9772
10189
  ] });
9773
10190
  }
9774
10191
  function MicOffIcon(props) {
9775
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10192
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9776
10193
  /* @__PURE__ */ jsx4("path", { d: "M2 2l20 20" }),
9777
10194
  /* @__PURE__ */ jsx4("path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12" }),
9778
10195
  /* @__PURE__ */ jsx4("path", { d: "M15 9.34V5a3 3 0 0 0-5.94-.6" }),
@@ -9781,20 +10198,20 @@ function MicOffIcon(props) {
9781
10198
  ] });
9782
10199
  }
9783
10200
  function CameraIcon(props) {
9784
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10201
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9785
10202
  /* @__PURE__ */ jsx4("path", { d: "m23 7-7 5 7 5V7Z" }),
9786
10203
  /* @__PURE__ */ jsx4("rect", { x: "1", y: "5", width: "15", height: "14", rx: "2" })
9787
10204
  ] });
9788
10205
  }
9789
10206
  function CameraOffIcon(props) {
9790
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10207
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9791
10208
  /* @__PURE__ */ jsx4("path", { d: "M2 2l20 20" }),
9792
10209
  /* @__PURE__ */ jsx4("path", { d: "M10.66 5H14a2 2 0 0 1 2 2v3.34l1 1L23 7v10" }),
9793
10210
  /* @__PURE__ */ jsx4("path", { d: "M16 16a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h2" })
9794
10211
  ] });
9795
10212
  }
9796
10213
  function ScreenShareIcon(props) {
9797
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10214
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9798
10215
  /* @__PURE__ */ jsx4("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2" }),
9799
10216
  /* @__PURE__ */ jsx4("path", { d: "M8 21h8M12 17v4" }),
9800
10217
  /* @__PURE__ */ jsx4("path", { d: "M12 13V8m0 0-2.5 2.5M12 8l2.5 2.5" })
@@ -9807,7 +10224,7 @@ function PhoneOffIcon(props) {
9807
10224
  return /* @__PURE__ */ jsx4(Icon, { fill: "currentColor", stroke: "none", ...props, children: /* @__PURE__ */ jsx4("path", { d: "M12 9c-1.6 0-3.15.25-4.6.72v3.1c0 .39-.23.74-.56.9-.98.49-1.87 1.12-2.66 1.85-.18.18-.43.28-.7.28-.28 0-.53-.11-.71-.29L.29 13.08a.99.99 0 0 1-.29-.7c0-.28.11-.53.29-.71C3.34 8.78 7.46 7 12 7s8.66 1.78 11.71 4.67c.18.18.29.43.29.71 0 .28-.11.53-.29.71l-2.48 2.48c-.18.18-.43.29-.71.29-.27 0-.52-.11-.7-.28a11.27 11.27 0 0 0-2.67-1.85.996.996 0 0 1-.56-.9v-3.1C15.15 9.25 13.6 9 12 9Z" }) });
9808
10225
  }
9809
10226
  function HandIcon(props) {
9810
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10227
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9811
10228
  /* @__PURE__ */ jsx4("path", { d: "M18 11V6a2 2 0 0 0-4 0v5" }),
9812
10229
  /* @__PURE__ */ jsx4("path", { d: "M14 10V4a2 2 0 0 0-4 0v7" }),
9813
10230
  /* @__PURE__ */ jsx4("path", { d: "M10 10.5V6a2 2 0 0 0-4 0v8" }),
@@ -9818,7 +10235,7 @@ function ChatIcon(props) {
9818
10235
  return /* @__PURE__ */ jsx4(Icon, { ...props, children: /* @__PURE__ */ jsx4("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2Z" }) });
9819
10236
  }
9820
10237
  function PeopleIcon(props) {
9821
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10238
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9822
10239
  /* @__PURE__ */ jsx4("path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }),
9823
10240
  /* @__PURE__ */ jsx4("circle", { cx: "9", cy: "7", r: "4" }),
9824
10241
  /* @__PURE__ */ jsx4("path", { d: "M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75" })
@@ -9828,19 +10245,31 @@ function RecordIcon(props) {
9828
10245
  return /* @__PURE__ */ jsx4(Icon, { ...props, children: /* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "8", fill: "currentColor", stroke: "none" }) });
9829
10246
  }
9830
10247
  function SparklesIcon(props) {
9831
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10248
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9832
10249
  /* @__PURE__ */ jsx4("path", { d: "M12 3l1.9 4.6L18.5 9.5l-4.6 1.9L12 16l-1.9-4.6L5.5 9.5l4.6-1.9L12 3Z" }),
9833
10250
  /* @__PURE__ */ jsx4("path", { d: "M19 15l.9 2.1 2.1.9-2.1.9-.9 2.1-.9-2.1-2.1-.9 2.1-.9L19 15Z" })
9834
10251
  ] });
9835
10252
  }
10253
+ function CaptionsIcon(props) {
10254
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
10255
+ /* @__PURE__ */ jsx4("rect", { x: "3", y: "5", width: "18", height: "14", rx: "2" }),
10256
+ /* @__PURE__ */ jsx4("path", { d: "M10 10.5a2 2 0 1 0 0 3M16 10.5a2 2 0 1 0 0 3" })
10257
+ ] });
10258
+ }
10259
+ function NotesIcon(props) {
10260
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
10261
+ /* @__PURE__ */ jsx4("path", { d: "M4 20l1-4L16.5 4.5a2.1 2.1 0 0 1 3 3L8 19l-4 1Z" }),
10262
+ /* @__PURE__ */ jsx4("path", { d: "M14 7l3 3" })
10263
+ ] });
10264
+ }
9836
10265
  function LockIcon(props) {
9837
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10266
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9838
10267
  /* @__PURE__ */ jsx4("rect", { x: "4", y: "10", width: "16", height: "11", rx: "2" }),
9839
10268
  /* @__PURE__ */ jsx4("path", { d: "M8 10V7a4 4 0 0 1 8 0v3" })
9840
10269
  ] });
9841
10270
  }
9842
10271
  function SettingsIcon(props) {
9843
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10272
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9844
10273
  /* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "3" }),
9845
10274
  /* @__PURE__ */ jsx4("path", { d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.6a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z" })
9846
10275
  ] });
@@ -9852,13 +10281,13 @@ function CloseIcon(props) {
9852
10281
  return /* @__PURE__ */ jsx4(Icon, { ...props, children: /* @__PURE__ */ jsx4("path", { d: "M18 6 6 18M6 6l12 12" }) });
9853
10282
  }
9854
10283
  function PinIcon(props) {
9855
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10284
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9856
10285
  /* @__PURE__ */ jsx4("path", { d: "M12 17v5" }),
9857
10286
  /* @__PURE__ */ jsx4("path", { d: "M9 2h6l-1 6 3.5 3.5V14H6.5v-2.5L10 8 9 2Z" })
9858
10287
  ] });
9859
10288
  }
9860
10289
  function SmileIcon(props) {
9861
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10290
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9862
10291
  /* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "10" }),
9863
10292
  /* @__PURE__ */ jsx4("path", { d: "M8 14s1.5 2 4 2 4-2 4-2" }),
9864
10293
  /* @__PURE__ */ jsx4("line", { x1: "9", x2: "9.01", y1: "9", y2: "9" }),
@@ -9866,20 +10295,20 @@ function SmileIcon(props) {
9866
10295
  ] });
9867
10296
  }
9868
10297
  function MoreIcon(props) {
9869
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10298
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9870
10299
  /* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "1" }),
9871
10300
  /* @__PURE__ */ jsx4("circle", { cx: "19", cy: "12", r: "1" }),
9872
10301
  /* @__PURE__ */ jsx4("circle", { cx: "5", cy: "12", r: "1" })
9873
10302
  ] });
9874
10303
  }
9875
10304
  function SearchIcon(props) {
9876
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10305
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9877
10306
  /* @__PURE__ */ jsx4("circle", { cx: "11", cy: "11", r: "8" }),
9878
10307
  /* @__PURE__ */ jsx4("path", { d: "m21 21-4.3-4.3" })
9879
10308
  ] });
9880
10309
  }
9881
10310
  function KeyboardIcon(props) {
9882
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10311
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9883
10312
  /* @__PURE__ */ jsx4("path", { d: "M10 8h.01M12 12h.01M14 8h.01M16 12h.01M18 8h.01M6 8h.01M7 16h10M8 12h.01" }),
9884
10313
  /* @__PURE__ */ jsx4("rect", { width: "20", height: "16", x: "2", y: "4", rx: "2" })
9885
10314
  ] });
@@ -9888,7 +10317,7 @@ function SpotlightIcon(props) {
9888
10317
  return /* @__PURE__ */ jsx4(Icon, { ...props, children: /* @__PURE__ */ jsx4("path", { d: "M12 2 15 8.5 22 9.3 17 14 18.2 21 12 17.6 5.8 21 7 14 2 9.3 9 8.5Z" }) });
9889
10318
  }
9890
10319
  function SignalIcon({ bars, ...props }) {
9891
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10320
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9892
10321
  /* @__PURE__ */ jsx4("path", { d: "M4 20v-2", opacity: bars >= 1 ? 1 : 0.3 }),
9893
10322
  /* @__PURE__ */ jsx4("path", { d: "M10 20v-6", opacity: bars >= 2 ? 1 : 0.3 }),
9894
10323
  /* @__PURE__ */ jsx4("path", { d: "M16 20V9", opacity: bars >= 3 ? 1 : 0.3 }),
@@ -9896,14 +10325,14 @@ function SignalIcon({ bars, ...props }) {
9896
10325
  ] });
9897
10326
  }
9898
10327
  function SpeakerIcon(props) {
9899
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10328
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9900
10329
  /* @__PURE__ */ jsx4("path", { d: "M11 5 6 9H2v6h4l5 4V5Z" }),
9901
10330
  /* @__PURE__ */ jsx4("path", { d: "M15.54 8.46a5 5 0 0 1 0 7.07" }),
9902
10331
  /* @__PURE__ */ jsx4("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14" })
9903
10332
  ] });
9904
10333
  }
9905
10334
  function FlagIcon(props) {
9906
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10335
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9907
10336
  /* @__PURE__ */ jsx4("path", { d: "M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z" }),
9908
10337
  /* @__PURE__ */ jsx4("line", { x1: "4", x2: "4", y1: "22", y2: "15" })
9909
10338
  ] });
@@ -9912,35 +10341,35 @@ function PencilIcon(props) {
9912
10341
  return /* @__PURE__ */ jsx4(Icon, { ...props, children: /* @__PURE__ */ jsx4("path", { d: "M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" }) });
9913
10342
  }
9914
10343
  function ActivitiesIcon(props) {
9915
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10344
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9916
10345
  /* @__PURE__ */ jsx4("path", { d: "M8.3 10a.7.7 0 0 1-.626-1.079L11.4 3a.7.7 0 0 1 1.198-.043L16.3 8.9a.7.7 0 0 1-.572 1.1Z" }),
9917
10346
  /* @__PURE__ */ jsx4("rect", { x: "3", y: "14", width: "7", height: "7", rx: "1" }),
9918
10347
  /* @__PURE__ */ jsx4("circle", { cx: "17.5", cy: "17.5", r: "3.5" })
9919
10348
  ] });
9920
10349
  }
9921
10350
  function PollIcon(props) {
9922
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10351
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9923
10352
  /* @__PURE__ */ jsx4("line", { x1: "18", x2: "18", y1: "20", y2: "10" }),
9924
10353
  /* @__PURE__ */ jsx4("line", { x1: "12", x2: "12", y1: "20", y2: "4" }),
9925
10354
  /* @__PURE__ */ jsx4("line", { x1: "6", x2: "6", y1: "20", y2: "14" })
9926
10355
  ] });
9927
10356
  }
9928
10357
  function QuestionIcon(props) {
9929
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10358
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9930
10359
  /* @__PURE__ */ jsx4("path", { d: "M7.9 20A9 9 0 1 0 4 16.1L2 22Z" }),
9931
10360
  /* @__PURE__ */ jsx4("path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" }),
9932
10361
  /* @__PURE__ */ jsx4("path", { d: "M12 17h.01" })
9933
10362
  ] });
9934
10363
  }
9935
10364
  function WhiteboardIcon(props) {
9936
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10365
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9937
10366
  /* @__PURE__ */ jsx4("path", { d: "M2 3h20" }),
9938
10367
  /* @__PURE__ */ jsx4("path", { d: "M21 3v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V3" }),
9939
10368
  /* @__PURE__ */ jsx4("path", { d: "m7 21 5-5 5 5" })
9940
10369
  ] });
9941
10370
  }
9942
10371
  function BreakoutIcon(props) {
9943
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10372
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9944
10373
  /* @__PURE__ */ jsx4("rect", { width: "7", height: "18", x: "3", y: "3", rx: "1" }),
9945
10374
  /* @__PURE__ */ jsx4("rect", { width: "7", height: "7", x: "14", y: "3", rx: "1" }),
9946
10375
  /* @__PURE__ */ jsx4("rect", { width: "7", height: "7", x: "14", y: "14", rx: "1" })
@@ -9950,29 +10379,32 @@ function UpvoteIcon(props) {
9950
10379
  return /* @__PURE__ */ jsx4(Icon, { ...props, children: /* @__PURE__ */ jsx4("path", { d: "M9 18v-6H5l7-7 7 7h-4v6H9z" }) });
9951
10380
  }
9952
10381
  function EraserIcon(props) {
9953
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10382
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9954
10383
  /* @__PURE__ */ jsx4("path", { d: "m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21" }),
9955
10384
  /* @__PURE__ */ jsx4("path", { d: "M22 21H7" }),
9956
10385
  /* @__PURE__ */ jsx4("path", { d: "m5 11 9 9" })
9957
10386
  ] });
9958
10387
  }
9959
10388
  function HighlighterIcon(props) {
9960
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10389
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9961
10390
  /* @__PURE__ */ jsx4("path", { d: "m9 11-6 6v3h9l3-3" }),
9962
10391
  /* @__PURE__ */ jsx4("path", { d: "m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4" })
9963
10392
  ] });
9964
10393
  }
9965
10394
  function UndoIcon(props) {
9966
- return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
10395
+ return /* @__PURE__ */ jsxs2(Icon, { ...props, children: [
9967
10396
  /* @__PURE__ */ jsx4("path", { d: "M9 14 4 9l5-5" }),
9968
10397
  /* @__PURE__ */ jsx4("path", { d: "M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11" })
9969
10398
  ] });
9970
10399
  }
10400
+ function ChevronUpIcon(props) {
10401
+ return /* @__PURE__ */ jsx4(Icon, { ...props, children: /* @__PURE__ */ jsx4("path", { d: "m6 15 6-6 6 6" }) });
10402
+ }
9971
10403
 
9972
10404
  // src/skins/meet/collab.tsx
9973
- import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
10405
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
9974
10406
  function useNotice() {
9975
- const [notice, setNotice] = useState3(null);
10407
+ const [notice, setNotice] = useState4(null);
9976
10408
  useEffect5(() => {
9977
10409
  if (notice === null) return void 0;
9978
10410
  const timer = setTimeout(() => setNotice(null), notice.failed ? 7e3 : 3e3);
@@ -10001,16 +10433,16 @@ function ActivitiesControl({
10001
10433
  active,
10002
10434
  onOpen
10003
10435
  }) {
10004
- const [open, setOpen] = useState3(false);
10436
+ const [open, setOpen] = useState4(false);
10005
10437
  const available = useAvailableActivities();
10006
10438
  const attention = useActivityAttention();
10007
10439
  if (available.length === 0) return null;
10008
- return /* @__PURE__ */ jsxs2("span", { className: "mx-meet__anchor", children: [
10009
- /* @__PURE__ */ jsxs2(ControlButton, { label: "Activities", active: open || active !== null, onClick: () => setOpen(!open), children: [
10440
+ return /* @__PURE__ */ jsxs3("span", { className: "mx-meet__anchor", children: [
10441
+ /* @__PURE__ */ jsxs3(ControlButton, { label: "Activities", active: open || active !== null, onClick: () => setOpen(!open), children: [
10010
10442
  /* @__PURE__ */ jsx5(ActivitiesIcon, {}),
10011
10443
  attention ? /* @__PURE__ */ jsx5("span", { className: "mx-meet__dot", "aria-hidden": "true" }) : null
10012
10444
  ] }),
10013
- open ? /* @__PURE__ */ jsx5(Popover, { label: "Activities", onClose: () => setOpen(false), children: /* @__PURE__ */ jsx5("div", { className: "mx-meet__menu", role: "menu", children: available.map((panel) => /* @__PURE__ */ jsxs2(
10445
+ open ? /* @__PURE__ */ jsx5(Popover, { label: "Activities", onClose: () => setOpen(false), children: /* @__PURE__ */ jsx5("div", { className: "mx-meet__menu", role: "menu", children: available.map((panel) => /* @__PURE__ */ jsxs3(
10014
10446
  MenuItem,
10015
10447
  {
10016
10448
  onSelect: () => {
@@ -10029,11 +10461,11 @@ function ActivitiesControl({
10029
10461
  }
10030
10462
  function PollComposer({ maxOptions, onDone }) {
10031
10463
  const { act } = useCollab();
10032
- const [question, setQuestion] = useState3("");
10033
- const [options, setOptions] = useState3(["", ""]);
10034
- const [multiple, setMultiple] = useState3(false);
10035
- const [anonymous, setAnonymous] = useState3(false);
10036
- const [busy, setBusy] = useState3(false);
10464
+ const [question, setQuestion] = useState4("");
10465
+ const [options, setOptions] = useState4(["", ""]);
10466
+ const [multiple, setMultiple] = useState4(false);
10467
+ const [anonymous, setAnonymous] = useState4(false);
10468
+ const [busy, setBusy] = useState4(false);
10037
10469
  const [notice, show] = useNotice();
10038
10470
  const filled = options.map((o) => o.trim()).filter(Boolean);
10039
10471
  const ready = question.trim().length > 0 && filled.length >= 2;
@@ -10056,7 +10488,7 @@ function PollComposer({ maxOptions, onDone }) {
10056
10488
  setBusy(false);
10057
10489
  onDone();
10058
10490
  };
10059
- return /* @__PURE__ */ jsxs2(
10491
+ return /* @__PURE__ */ jsxs3(
10060
10492
  "form",
10061
10493
  {
10062
10494
  className: "mx-meet__collab-card",
@@ -10065,7 +10497,7 @@ function PollComposer({ maxOptions, onDone }) {
10065
10497
  if (ready) void save(true);
10066
10498
  },
10067
10499
  children: [
10068
- /* @__PURE__ */ jsxs2("label", { className: "mx-meet__field", children: [
10500
+ /* @__PURE__ */ jsxs3("label", { className: "mx-meet__field", children: [
10069
10501
  /* @__PURE__ */ jsx5("span", { className: "mx-meet__field-label", children: "Question" }),
10070
10502
  /* @__PURE__ */ jsx5(
10071
10503
  "input",
@@ -10079,7 +10511,7 @@ function PollComposer({ maxOptions, onDone }) {
10079
10511
  }
10080
10512
  )
10081
10513
  ] }),
10082
- options.map((option, index) => /* @__PURE__ */ jsxs2("div", { className: "mx-meet__composer-row", children: [
10514
+ options.map((option, index) => /* @__PURE__ */ jsxs3("div", { className: "mx-meet__composer-row", children: [
10083
10515
  /* @__PURE__ */ jsx5(
10084
10516
  "input",
10085
10517
  {
@@ -10094,16 +10526,16 @@ function PollComposer({ maxOptions, onDone }) {
10094
10526
  options.length > 2 ? /* @__PURE__ */ jsx5(ControlButton, { label: `Remove answer ${index + 1}`, onClick: () => setOptions(options.filter((_, i) => i !== index)), children: /* @__PURE__ */ jsx5(CloseIcon, { width: 14, height: 14 }) }) : null
10095
10527
  ] }, index)),
10096
10528
  options.length < maxOptions ? /* @__PURE__ */ jsx5("button", { type: "button", className: "mx-meet__link", onClick: () => setOptions([...options, ""]), children: "Add an answer" }) : null,
10097
- /* @__PURE__ */ jsxs2("label", { className: "mx-meet__toggle", children: [
10529
+ /* @__PURE__ */ jsxs3("label", { className: "mx-meet__toggle", children: [
10098
10530
  /* @__PURE__ */ jsx5("input", { type: "checkbox", checked: multiple, onChange: (event) => setMultiple(event.target.checked) }),
10099
10531
  "People can pick more than one"
10100
10532
  ] }),
10101
- /* @__PURE__ */ jsxs2("label", { className: "mx-meet__toggle", children: [
10533
+ /* @__PURE__ */ jsxs3("label", { className: "mx-meet__toggle", children: [
10102
10534
  /* @__PURE__ */ jsx5("input", { type: "checkbox", checked: anonymous, onChange: (event) => setAnonymous(event.target.checked) }),
10103
10535
  "Anonymous \u2014 nobody sees who picked what"
10104
10536
  ] }),
10105
10537
  /* @__PURE__ */ jsx5(Notice, { notice }),
10106
- /* @__PURE__ */ jsxs2("div", { className: "mx-meet__collab-actions", children: [
10538
+ /* @__PURE__ */ jsxs3("div", { className: "mx-meet__collab-actions", children: [
10107
10539
  /* @__PURE__ */ jsx5("button", { type: "submit", className: "mx-meet__primary mx-meet__primary--small", disabled: !ready || busy, children: "Launch" }),
10108
10540
  /* @__PURE__ */ jsx5("button", { type: "button", className: "mx-meet__chip", disabled: !ready || busy, onClick: () => void save(false), children: "Save draft" }),
10109
10541
  /* @__PURE__ */ jsx5("button", { type: "button", className: "mx-meet__link", onClick: onDone, children: "Cancel" })
@@ -10114,7 +10546,7 @@ function PollComposer({ maxOptions, onDone }) {
10114
10546
  }
10115
10547
  function PollCard({ poll }) {
10116
10548
  const { act, isHost } = useCollab();
10117
- const [picked, setPicked] = useState3(poll.myVote.slice());
10549
+ const [picked, setPicked] = useState4(poll.myVote.slice());
10118
10550
  const [notice, show] = useNotice();
10119
10551
  useEffect5(() => setPicked(poll.myVote.slice()), [poll.myVote]);
10120
10552
  const counts = poll.options.some((o) => o.votes !== void 0);
@@ -10126,12 +10558,12 @@ function PollCard({ poll }) {
10126
10558
  if (!canVote) return;
10127
10559
  setPicked(poll.multiple ? picked.includes(id) ? picked.filter((p) => p !== id) : [...picked, id] : [id]);
10128
10560
  };
10129
- return /* @__PURE__ */ jsxs2("article", { className: "mx-meet__collab-card", "aria-label": poll.question, children: [
10130
- /* @__PURE__ */ jsxs2("header", { className: "mx-meet__collab-head", children: [
10561
+ return /* @__PURE__ */ jsxs3("article", { className: "mx-meet__collab-card", "aria-label": poll.question, children: [
10562
+ /* @__PURE__ */ jsxs3("header", { className: "mx-meet__collab-head", children: [
10131
10563
  /* @__PURE__ */ jsx5("strong", { className: "mx-meet__collab-title", children: poll.question }),
10132
10564
  /* @__PURE__ */ jsx5("span", { className: "mx-meet__collab-state", children: stateWord })
10133
10565
  ] }),
10134
- /* @__PURE__ */ jsxs2("p", { className: "mx-meet__panel-note", children: [
10566
+ /* @__PURE__ */ jsxs3("p", { className: "mx-meet__panel-note", children: [
10135
10567
  poll.multiple ? "Pick any." : "Pick one.",
10136
10568
  poll.anonymous ? " Anonymous." : "",
10137
10569
  counts && poll.voterCount !== null ? ` ${poll.voterCount} voted.` : "",
@@ -10140,8 +10572,8 @@ function PollCard({ poll }) {
10140
10572
  /* @__PURE__ */ jsx5("ul", { className: "mx-meet__poll-options", children: poll.options.map((option) => {
10141
10573
  const share = counts && total > 0 ? Math.round((option.votes ?? 0) / total * 100) : 0;
10142
10574
  const chosen = picked.includes(option.id);
10143
- return /* @__PURE__ */ jsxs2("li", { children: [
10144
- /* @__PURE__ */ jsxs2(
10575
+ return /* @__PURE__ */ jsxs3("li", { children: [
10576
+ /* @__PURE__ */ jsxs3(
10145
10577
  "button",
10146
10578
  {
10147
10579
  type: "button",
@@ -10152,7 +10584,7 @@ function PollCard({ poll }) {
10152
10584
  children: [
10153
10585
  counts ? /* @__PURE__ */ jsx5("span", { className: "mx-meet__poll-bar", style: { width: `${share}%` }, "aria-hidden": "true" }) : null,
10154
10586
  /* @__PURE__ */ jsx5("span", { className: "mx-meet__poll-text", children: option.text }),
10155
- counts ? /* @__PURE__ */ jsxs2("span", { className: "mx-meet__poll-count", children: [
10587
+ counts ? /* @__PURE__ */ jsxs3("span", { className: "mx-meet__poll-count", children: [
10156
10588
  option.votes ?? 0,
10157
10589
  " \xB7 ",
10158
10590
  share,
@@ -10165,7 +10597,7 @@ function PollCard({ poll }) {
10165
10597
  ] }, option.id);
10166
10598
  }) }),
10167
10599
  /* @__PURE__ */ jsx5(Notice, { notice }),
10168
- /* @__PURE__ */ jsxs2("div", { className: "mx-meet__collab-actions", children: [
10600
+ /* @__PURE__ */ jsxs3("div", { className: "mx-meet__collab-actions", children: [
10169
10601
  canVote ? /* @__PURE__ */ jsx5(
10170
10602
  "button",
10171
10603
  {
@@ -10185,11 +10617,11 @@ function PollCard({ poll }) {
10185
10617
  }
10186
10618
  function PollsPanel({ onClose }) {
10187
10619
  const { collab, isHost } = useCollab();
10188
- const [composing, setComposing] = useState3(false);
10620
+ const [composing, setComposing] = useState4(false);
10189
10621
  const polls = collab?.polls ?? [];
10190
10622
  const maxOptions = collab?.settings?.pollMaxOptions ?? 10;
10191
10623
  const ordered = [...polls].reverse();
10192
- return /* @__PURE__ */ jsxs2(PanelShell, { title: "Polls", onClose, children: [
10624
+ return /* @__PURE__ */ jsxs3(PanelShell, { title: "Polls", onClose, children: [
10193
10625
  collab?.error != null ? /* @__PURE__ */ jsx5("p", { className: "mx-meet__panel-note mx-meet__collab-error", children: collab.error }) : null,
10194
10626
  isHost && !composing ? /* @__PURE__ */ jsx5("button", { type: "button", className: "mx-meet__primary mx-meet__primary--small", onClick: () => setComposing(true), children: "New poll" }) : null,
10195
10627
  isHost && composing ? /* @__PURE__ */ jsx5(PollComposer, { maxOptions, onDone: () => setComposing(false) }) : null,
@@ -10199,12 +10631,12 @@ function PollsPanel({ onClose }) {
10199
10631
  }
10200
10632
  function QuestionCard({ question }) {
10201
10633
  const { act, isHost } = useCollab();
10202
- const [answering, setAnswering] = useState3(false);
10203
- const [reply, setReply] = useState3("");
10634
+ const [answering, setAnswering] = useState4(false);
10635
+ const [reply, setReply] = useState4("");
10204
10636
  const [notice, show] = useNotice();
10205
- return /* @__PURE__ */ jsxs2("li", { className: `mx-meet__collab-card mx-meet__question mx-meet__question--${question.state}`, children: [
10206
- /* @__PURE__ */ jsxs2("div", { className: "mx-meet__question-row", children: [
10207
- /* @__PURE__ */ jsxs2(
10637
+ return /* @__PURE__ */ jsxs3("li", { className: `mx-meet__collab-card mx-meet__question mx-meet__question--${question.state}`, children: [
10638
+ /* @__PURE__ */ jsxs3("div", { className: "mx-meet__question-row", children: [
10639
+ /* @__PURE__ */ jsxs3(
10208
10640
  "button",
10209
10641
  {
10210
10642
  type: "button",
@@ -10219,15 +10651,15 @@ function QuestionCard({ question }) {
10219
10651
  ]
10220
10652
  }
10221
10653
  ),
10222
- /* @__PURE__ */ jsxs2("div", { className: "mx-meet__question-body", children: [
10223
- /* @__PURE__ */ jsxs2("span", { className: "mx-meet__chat-meta", children: [
10654
+ /* @__PURE__ */ jsxs3("div", { className: "mx-meet__question-body", children: [
10655
+ /* @__PURE__ */ jsxs3("span", { className: "mx-meet__chat-meta", children: [
10224
10656
  /* @__PURE__ */ jsx5("strong", { children: question.mine ? "You" : question.askerName }),
10225
10657
  question.mine && question.anonymous ? /* @__PURE__ */ jsx5("span", { className: "mx-meet__chat-private", children: "anonymous" }) : null,
10226
10658
  question.state !== "open" ? /* @__PURE__ */ jsx5("span", { className: "mx-meet__collab-state", children: question.state === "answered" ? "Answered" : "Dismissed" }) : null
10227
10659
  ] }),
10228
10660
  /* @__PURE__ */ jsx5("span", { className: "mx-meet__chat-text", children: question.text }),
10229
- question.answer !== null ? /* @__PURE__ */ jsxs2("span", { className: "mx-meet__question-answer", children: [
10230
- /* @__PURE__ */ jsxs2("strong", { children: [
10661
+ question.answer !== null ? /* @__PURE__ */ jsxs3("span", { className: "mx-meet__question-answer", children: [
10662
+ /* @__PURE__ */ jsxs3("strong", { children: [
10231
10663
  question.answeredByName ?? "Host",
10232
10664
  ":"
10233
10665
  ] }),
@@ -10236,7 +10668,7 @@ function QuestionCard({ question }) {
10236
10668
  ] }) : null
10237
10669
  ] })
10238
10670
  ] }),
10239
- answering ? /* @__PURE__ */ jsxs2(
10671
+ answering ? /* @__PURE__ */ jsxs3(
10240
10672
  "form",
10241
10673
  {
10242
10674
  className: "mx-meet__composer-row",
@@ -10268,7 +10700,7 @@ function QuestionCard({ question }) {
10268
10700
  }
10269
10701
  ) : null,
10270
10702
  /* @__PURE__ */ jsx5(Notice, { notice }),
10271
- isHost || question.mine ? /* @__PURE__ */ jsxs2("div", { className: "mx-meet__collab-actions", children: [
10703
+ isHost || question.mine ? /* @__PURE__ */ jsxs3("div", { className: "mx-meet__collab-actions", children: [
10272
10704
  isHost && question.state === "open" && !answering ? /* @__PURE__ */ jsx5("button", { type: "button", className: "mx-meet__chip", onClick: () => setAnswering(true), children: "Answer" }) : null,
10273
10705
  isHost && question.state === "open" ? /* @__PURE__ */ jsx5("button", { type: "button", className: "mx-meet__chip", onClick: () => void act({ action: "question_dismiss", itemId: question.id }).then(show), children: "Dismiss" }) : null,
10274
10706
  isHost && question.state !== "open" ? /* @__PURE__ */ jsx5("button", { type: "button", className: "mx-meet__chip", onClick: () => void act({ action: "question_reopen", itemId: question.id }).then(show), children: "Reopen" }) : null,
@@ -10278,9 +10710,9 @@ function QuestionCard({ question }) {
10278
10710
  }
10279
10711
  function QaPanel({ onClose }) {
10280
10712
  const { collab, act } = useCollab();
10281
- const [draft, setDraft] = useState3("");
10282
- const [anonymous, setAnonymous] = useState3(false);
10283
- const [order, setOrder] = useState3("popular");
10713
+ const [draft, setDraft] = useState4("");
10714
+ const [anonymous, setAnonymous] = useState4(false);
10715
+ const [order, setOrder] = useState4("popular");
10284
10716
  const [notice, show] = useNotice();
10285
10717
  const allowAnonymous = collab?.settings?.qaAnonymousAllowed === true;
10286
10718
  const questions = [...collab?.questions ?? []];
@@ -10288,9 +10720,9 @@ function QaPanel({ onClose }) {
10288
10720
  questions.sort(
10289
10721
  (a, b) => rank(a) !== rank(b) ? rank(a) - rank(b) : order === "popular" ? b.upvotes - a.upvotes || (b.createdAt ?? "").localeCompare(a.createdAt ?? "") : (b.createdAt ?? "").localeCompare(a.createdAt ?? "")
10290
10722
  );
10291
- return /* @__PURE__ */ jsxs2(PanelShell, { title: "Q&A", onClose, children: [
10723
+ return /* @__PURE__ */ jsxs3(PanelShell, { title: "Q&A", onClose, children: [
10292
10724
  collab?.error != null ? /* @__PURE__ */ jsx5("p", { className: "mx-meet__panel-note mx-meet__collab-error", children: collab.error }) : null,
10293
- /* @__PURE__ */ jsxs2(
10725
+ /* @__PURE__ */ jsxs3(
10294
10726
  "form",
10295
10727
  {
10296
10728
  className: "mx-meet__composer mx-meet__composer--chat",
@@ -10304,7 +10736,7 @@ function QaPanel({ onClose }) {
10304
10736
  });
10305
10737
  },
10306
10738
  children: [
10307
- /* @__PURE__ */ jsxs2("div", { className: "mx-meet__composer-row", children: [
10739
+ /* @__PURE__ */ jsxs3("div", { className: "mx-meet__composer-row", children: [
10308
10740
  /* @__PURE__ */ jsx5(
10309
10741
  "input",
10310
10742
  {
@@ -10318,7 +10750,7 @@ function QaPanel({ onClose }) {
10318
10750
  ),
10319
10751
  /* @__PURE__ */ jsx5("button", { type: "submit", className: "mx-meet__send", disabled: draft.trim().length === 0, children: "Ask" })
10320
10752
  ] }),
10321
- allowAnonymous ? /* @__PURE__ */ jsxs2("label", { className: "mx-meet__toggle", children: [
10753
+ allowAnonymous ? /* @__PURE__ */ jsxs3("label", { className: "mx-meet__toggle", children: [
10322
10754
  /* @__PURE__ */ jsx5("input", { type: "checkbox", checked: anonymous, onChange: (event) => setAnonymous(event.target.checked) }),
10323
10755
  "Ask anonymously"
10324
10756
  ] }) : null
@@ -10369,10 +10801,10 @@ function StrokeView({ stroke, onPick }) {
10369
10801
  }
10370
10802
  function WhiteboardPanel({ onClose }) {
10371
10803
  const { collab, act, isHost, localIdentity } = useCollab();
10372
- const [tool, setTool] = useState3("pen");
10373
- const [color, setColor] = useState3(INK[0]);
10374
- const [drawing, setDrawing] = useState3(null);
10375
- const [pending, setPending] = useState3([]);
10804
+ const [tool, setTool] = useState4("pen");
10805
+ const [color, setColor] = useState4(INK[0]);
10806
+ const [drawing, setDrawing] = useState4(null);
10807
+ const [pending, setPending] = useState4([]);
10376
10808
  const [notice, show] = useNotice();
10377
10809
  const svgRef = useRef5(null);
10378
10810
  const strokes = collab?.strokes ?? [];
@@ -10406,8 +10838,8 @@ function WhiteboardPanel({ onClose }) {
10406
10838
  if (!result.ok) show(result);
10407
10839
  });
10408
10840
  };
10409
- return /* @__PURE__ */ jsxs2("section", { className: "mx-meet__whiteboard", "aria-label": "Whiteboard", children: [
10410
- /* @__PURE__ */ jsxs2("header", { className: "mx-meet__whiteboard-bar", children: [
10841
+ return /* @__PURE__ */ jsxs3("section", { className: "mx-meet__whiteboard", "aria-label": "Whiteboard", children: [
10842
+ /* @__PURE__ */ jsxs3("header", { className: "mx-meet__whiteboard-bar", children: [
10411
10843
  /* @__PURE__ */ jsx5("strong", { className: "mx-meet__panel-title", children: "Whiteboard" }),
10412
10844
  /* @__PURE__ */ jsx5("div", { className: "mx-meet__pills", role: "group", "aria-label": "Tool", children: [
10413
10845
  ["pen", "Pen", /* @__PURE__ */ jsx5(PencilIcon, { width: 16, height: 16 }, "p")],
@@ -10457,7 +10889,7 @@ function WhiteboardPanel({ onClose }) {
10457
10889
  /* @__PURE__ */ jsx5(ControlButton, { label: "Close whiteboard", onClick: onClose, children: /* @__PURE__ */ jsx5(CloseIcon, { width: 16, height: 16 }) })
10458
10890
  ] }),
10459
10891
  /* @__PURE__ */ jsx5(Notice, { notice }),
10460
- /* @__PURE__ */ jsxs2(
10892
+ /* @__PURE__ */ jsxs3(
10461
10893
  "svg",
10462
10894
  {
10463
10895
  ref: svgRef,
@@ -10515,7 +10947,7 @@ function clock(ms2) {
10515
10947
  }
10516
10948
  var BREAKOUT_EXPIRE_RETRY_MS = 5e3;
10517
10949
  function useNow(active) {
10518
- const [now, setNow] = useState3(() => Date.now());
10950
+ const [now, setNow] = useState4(() => Date.now());
10519
10951
  useEffect5(() => {
10520
10952
  if (!active) return void 0;
10521
10953
  const timer = setInterval(() => setNow(Date.now()), 1e3);
@@ -10530,11 +10962,11 @@ function BreakoutSetup() {
10530
10962
  const people = (snapshot?.participants ?? []).filter(
10531
10963
  (p) => !p.isAgent && p.role !== "host" && p.role !== "cohost"
10532
10964
  );
10533
- const [count2, setCount] = useState3(Math.min(Math.max(2, Math.ceil(people.length / 4)), settings?.breakoutMaxRooms ?? 20));
10534
- const [mode, setMode] = useState3("auto");
10535
- const [minutes, setMinutes] = useState3(settings?.breakoutDefaultMinutes ?? 15);
10536
- const [manual, setManual] = useState3({});
10537
- const [busy, setBusy] = useState3(false);
10965
+ const [count2, setCount] = useState4(Math.min(Math.max(2, Math.ceil(people.length / 4)), settings?.breakoutMaxRooms ?? 20));
10966
+ const [mode, setMode] = useState4("auto");
10967
+ const [minutes, setMinutes] = useState4(settings?.breakoutDefaultMinutes ?? 15);
10968
+ const [manual, setManual] = useState4({});
10969
+ const [busy, setBusy] = useState4(false);
10538
10970
  const [notice, show] = useNotice();
10539
10971
  const max = settings?.breakoutMaxRooms ?? 20;
10540
10972
  const rooms = Math.min(Math.max(1, count2), max);
@@ -10546,8 +10978,8 @@ function BreakoutSetup() {
10546
10978
  (_, index) => people.filter((p) => (manual[p.identity] ?? -1) === index).map((p) => p.identity)
10547
10979
  ) : Array.from({ length: rooms }, () => []);
10548
10980
  const nameOf = (identity) => people.find((p) => p.identity === identity)?.displayName ?? identity;
10549
- return /* @__PURE__ */ jsxs2("div", { className: "mx-meet__collab-card", children: [
10550
- /* @__PURE__ */ jsxs2("label", { className: "mx-meet__field", children: [
10981
+ return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__collab-card", children: [
10982
+ /* @__PURE__ */ jsxs3("label", { className: "mx-meet__field", children: [
10551
10983
  /* @__PURE__ */ jsx5("span", { className: "mx-meet__field-label", children: "Rooms" }),
10552
10984
  /* @__PURE__ */ jsx5(
10553
10985
  "input",
@@ -10576,7 +11008,7 @@ function BreakoutSetup() {
10576
11008
  },
10577
11009
  value
10578
11010
  )) }),
10579
- /* @__PURE__ */ jsxs2("label", { className: "mx-meet__field", children: [
11011
+ /* @__PURE__ */ jsxs3("label", { className: "mx-meet__field", children: [
10580
11012
  /* @__PURE__ */ jsx5("span", { className: "mx-meet__field-label", children: "Timer (minutes, 0 for none)" }),
10581
11013
  /* @__PURE__ */ jsx5(
10582
11014
  "input",
@@ -10590,9 +11022,9 @@ function BreakoutSetup() {
10590
11022
  }
10591
11023
  )
10592
11024
  ] }),
10593
- mode === "manual" ? people.length === 0 ? /* @__PURE__ */ jsx5("p", { className: "mx-meet__panel-note", children: "Nobody else is in the meeting yet." }) : /* @__PURE__ */ jsx5("ul", { className: "mx-meet__people", children: people.map((person) => /* @__PURE__ */ jsxs2("li", { className: "mx-meet__person", children: [
11025
+ mode === "manual" ? people.length === 0 ? /* @__PURE__ */ jsx5("p", { className: "mx-meet__panel-note", children: "Nobody else is in the meeting yet." }) : /* @__PURE__ */ jsx5("ul", { className: "mx-meet__people", children: people.map((person) => /* @__PURE__ */ jsxs3("li", { className: "mx-meet__person", children: [
10594
11026
  /* @__PURE__ */ jsx5("span", { className: "mx-meet__person-name", children: person.displayName }),
10595
- /* @__PURE__ */ jsxs2(
11027
+ /* @__PURE__ */ jsxs3(
10596
11028
  "select",
10597
11029
  {
10598
11030
  className: "mx-meet__select mx-meet__select--compact",
@@ -10601,15 +11033,15 @@ function BreakoutSetup() {
10601
11033
  onChange: (event) => setManual({ ...manual, [person.identity]: Number(event.target.value) }),
10602
11034
  children: [
10603
11035
  /* @__PURE__ */ jsx5("option", { value: -1, children: "Main room" }),
10604
- Array.from({ length: rooms }, (_, index) => /* @__PURE__ */ jsxs2("option", { value: index, children: [
11036
+ Array.from({ length: rooms }, (_, index) => /* @__PURE__ */ jsxs3("option", { value: index, children: [
10605
11037
  "Room ",
10606
11038
  index + 1
10607
11039
  ] }, index))
10608
11040
  ]
10609
11041
  }
10610
11042
  )
10611
- ] }, person.identity)) }) : mode === "auto" ? /* @__PURE__ */ jsx5("ul", { className: "mx-meet__breakout-rooms", children: assignment.map((members, index) => /* @__PURE__ */ jsxs2("li", { children: [
10612
- /* @__PURE__ */ jsxs2("strong", { children: [
11043
+ ] }, person.identity)) }) : mode === "auto" ? /* @__PURE__ */ jsx5("ul", { className: "mx-meet__breakout-rooms", children: assignment.map((members, index) => /* @__PURE__ */ jsxs3("li", { children: [
11044
+ /* @__PURE__ */ jsxs3("strong", { children: [
10613
11045
  "Room ",
10614
11046
  index + 1
10615
11047
  ] }),
@@ -10617,7 +11049,7 @@ function BreakoutSetup() {
10617
11049
  /* @__PURE__ */ jsx5("span", { className: "mx-meet__panel-note", children: members.length === 0 ? "empty" : members.map(nameOf).join(", ") })
10618
11050
  ] }, index)) }) : /* @__PURE__ */ jsx5("p", { className: "mx-meet__panel-note", children: "Everyone picks a room from their own screen." }),
10619
11051
  /* @__PURE__ */ jsx5(Notice, { notice }),
10620
- /* @__PURE__ */ jsxs2(
11052
+ /* @__PURE__ */ jsxs3(
10621
11053
  "button",
10622
11054
  {
10623
11055
  type: "button",
@@ -10650,7 +11082,7 @@ function BreakoutRun({ plan }) {
10650
11082
  const snapshot = useMeetSnapshot();
10651
11083
  const now = useNow(plan.endsAt !== null);
10652
11084
  const remaining = breakoutRemainingMs(plan, now);
10653
- const [broadcast, setBroadcast] = useState3("");
11085
+ const [broadcast, setBroadcast] = useState4("");
10654
11086
  const [notice, show] = useNotice();
10655
11087
  const nameOf = (identity) => displayNameFor(snapshot?.names ?? {}, identity, snapshot?.participants.find((p) => p.identity === identity)?.displayName);
10656
11088
  const mine = assignedRoom(plan, localIdentity);
@@ -10662,8 +11094,8 @@ function BreakoutRun({ plan }) {
10662
11094
  }
10663
11095
  show(await moveTo(roomId));
10664
11096
  };
10665
- return /* @__PURE__ */ jsxs2("div", { className: "mx-meet__collab-card", children: [
10666
- /* @__PURE__ */ jsxs2("p", { className: "mx-meet__panel-note", children: [
11097
+ return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__collab-card", children: [
11098
+ /* @__PURE__ */ jsxs3("p", { className: "mx-meet__panel-note", children: [
10667
11099
  plan.rooms.length,
10668
11100
  " room",
10669
11101
  plan.rooms.length === 1 ? "" : "s",
@@ -10674,15 +11106,15 @@ function BreakoutRun({ plan }) {
10674
11106
  /* @__PURE__ */ jsx5("ul", { className: "mx-meet__breakout-rooms", children: plan.rooms.map((room) => {
10675
11107
  const here = breakoutRoomId === room.id;
10676
11108
  const mayJoin = isHost || plan.allowChoose || mine?.id === room.id;
10677
- return /* @__PURE__ */ jsxs2("li", { className: here ? "mx-meet__breakout-room mx-meet__breakout-room--here" : "mx-meet__breakout-room", children: [
10678
- /* @__PURE__ */ jsxs2("div", { className: "mx-meet__collab-head", children: [
11109
+ return /* @__PURE__ */ jsxs3("li", { className: here ? "mx-meet__breakout-room mx-meet__breakout-room--here" : "mx-meet__breakout-room", children: [
11110
+ /* @__PURE__ */ jsxs3("div", { className: "mx-meet__collab-head", children: [
10679
11111
  /* @__PURE__ */ jsx5("strong", { children: room.name }),
10680
11112
  here ? /* @__PURE__ */ jsx5("span", { className: "mx-meet__collab-state", children: "You are here" }) : mayJoin ? /* @__PURE__ */ jsx5("button", { type: "button", className: "mx-meet__chip", onClick: () => void join(room.id), children: "Join" }) : null
10681
11113
  ] }),
10682
11114
  /* @__PURE__ */ jsx5("span", { className: "mx-meet__panel-note", children: room.identities.length === 0 ? "Nobody assigned" : room.identities.map(nameOf).join(", ") }),
10683
- isHost && room.identities.length > 0 ? /* @__PURE__ */ jsx5("ul", { className: "mx-meet__people", children: room.identities.map((identity) => /* @__PURE__ */ jsxs2("li", { className: "mx-meet__person", children: [
11115
+ isHost && room.identities.length > 0 ? /* @__PURE__ */ jsx5("ul", { className: "mx-meet__people", children: room.identities.map((identity) => /* @__PURE__ */ jsxs3("li", { className: "mx-meet__person", children: [
10684
11116
  /* @__PURE__ */ jsx5("span", { className: "mx-meet__person-name", children: nameOf(identity) }),
10685
- /* @__PURE__ */ jsxs2(
11117
+ /* @__PURE__ */ jsxs3(
10686
11118
  "select",
10687
11119
  {
10688
11120
  className: "mx-meet__select mx-meet__select--compact",
@@ -10703,7 +11135,7 @@ function BreakoutRun({ plan }) {
10703
11135
  ] }, room.id);
10704
11136
  }) }),
10705
11137
  breakoutRoomId !== null ? /* @__PURE__ */ jsx5("button", { type: "button", className: "mx-meet__chip", onClick: () => void join(null), children: "Return to the main room" }) : null,
10706
- isHost ? /* @__PURE__ */ jsxs2(
11138
+ isHost ? /* @__PURE__ */ jsxs3(
10707
11139
  "form",
10708
11140
  {
10709
11141
  className: "mx-meet__composer-row",
@@ -10739,7 +11171,7 @@ function BreakoutRun({ plan }) {
10739
11171
  type: "button",
10740
11172
  className: "mx-meet__primary mx-meet__primary--small",
10741
11173
  onClick: () => void act({ action: "breakout_close" }).then(show),
10742
- children: "Bring everyone back"
11174
+ children: "Close rooms"
10743
11175
  }
10744
11176
  ) : null
10745
11177
  ] });
@@ -10747,7 +11179,7 @@ function BreakoutRun({ plan }) {
10747
11179
  function BreakoutsPanel({ onClose }) {
10748
11180
  const { collab, isHost } = useCollab();
10749
11181
  const plan = collab?.breakout?.state === "open" ? collab.breakout : null;
10750
- return /* @__PURE__ */ jsxs2(PanelShell, { title: "Breakout rooms", onClose, children: [
11182
+ return /* @__PURE__ */ jsxs3(PanelShell, { title: "Breakout rooms", onClose, children: [
10751
11183
  collab?.error != null ? /* @__PURE__ */ jsx5("p", { className: "mx-meet__panel-note mx-meet__collab-error", children: collab.error }) : null,
10752
11184
  plan !== null ? /* @__PURE__ */ jsx5(BreakoutRun, { plan }) : isHost ? /* @__PURE__ */ jsx5(BreakoutSetup, {}) : /* @__PURE__ */ jsx5("p", { className: "mx-meet__panel-note", children: "Breakout rooms are closed." })
10753
11185
  ] });
@@ -10757,11 +11189,16 @@ function BreakoutBanner() {
10757
11189
  const { collab, act, moveTo, localIdentity, breakoutRoomId } = useCollab();
10758
11190
  const plan = collab?.breakout ?? null;
10759
11191
  const open = plan?.state === "open" ? plan : null;
10760
- const now = useNow(open?.endsAt != null);
11192
+ const now = useNow(open?.endsAt != null || open?.closingAt != null);
10761
11193
  const remaining = breakoutRemainingMs(open, now);
11194
+ const closing = breakoutClosingMs(open, now);
10762
11195
  const assigned = assignedRoom(open, localIdentity);
10763
- const [dismissedBroadcast, setDismissedBroadcast] = useState3(null);
10764
- const [failure, setFailure] = useState3(null);
11196
+ const [later, setLater] = useState4(null);
11197
+ const [autoSince, setAutoSince] = useState4(null);
11198
+ const AUTO_MOVE_MS = 1e4;
11199
+ const autoLeft = open?.moveMode === "auto" && assigned !== null && autoSince?.id === open.id && later !== open.id ? Math.max(0, AUTO_MOVE_MS - (now - autoSince.at)) : null;
11200
+ const [dismissedBroadcast, setDismissedBroadcast] = useState4(null);
11201
+ const [failure, setFailure] = useState4(null);
10765
11202
  const expireAsked = useRef5(null);
10766
11203
  const moveAt = collab?.moveRequest?.at ?? null;
10767
11204
  useEffect5(() => {
@@ -10772,6 +11209,19 @@ function BreakoutBanner() {
10772
11209
  if ((here ?? null) === request.roomId) return;
10773
11210
  void moveTo(request.roomId).then((result) => setFailure(result.ok ? null : result.message));
10774
11211
  }, [moveAt, host, moveTo]);
11212
+ const joinAssigned = async () => {
11213
+ if (assigned === null) return;
11214
+ const said = await act({ action: "breakout_join", roomId: assigned.id });
11215
+ const result = said.ok ? await moveTo(assigned.id) : said;
11216
+ setFailure(result.ok ? null : result.message);
11217
+ };
11218
+ useEffect5(() => {
11219
+ if (open === null || open.moveMode !== "auto" || assigned === null || breakoutRoomId !== null) return;
11220
+ if (autoSince?.id !== open.id) setAutoSince({ id: open.id, at: Date.now() });
11221
+ }, [open, assigned, breakoutRoomId, autoSince]);
11222
+ useEffect5(() => {
11223
+ if (autoLeft === 0) void joinAssigned();
11224
+ }, [autoLeft]);
10775
11225
  useEffect5(() => {
10776
11226
  if (open === null || remaining === null || remaining > 0) return;
10777
11227
  const asked = expireAsked.current;
@@ -10783,31 +11233,29 @@ function BreakoutBanner() {
10783
11233
  const showBroadcast = broadcast !== null && broadcast.at !== dismissedBroadcast && open !== null;
10784
11234
  const room = open?.rooms.find((r) => r.id === breakoutRoomId) ?? null;
10785
11235
  if (open === null && failure === null) return null;
10786
- return /* @__PURE__ */ jsxs2("div", { className: "mx-meet__breakout-banner", role: "status", children: [
11236
+ return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__breakout-banner", role: "status", children: [
10787
11237
  failure !== null ? /* @__PURE__ */ jsx5("span", { className: "mx-meet__collab-error", children: failure }) : null,
10788
- room !== null ? /* @__PURE__ */ jsxs2("span", { children: [
11238
+ closing !== null ? /* @__PURE__ */ jsxs3("span", { role: "status", "data-meet-notice": "breakout-closing", children: [
11239
+ "Rooms closing in ",
11240
+ Math.ceil(closing / 1e3),
11241
+ room !== null ? " \u2014 you return to the main meeting" : ""
11242
+ ] }) : null,
11243
+ room !== null ? /* @__PURE__ */ jsxs3("span", { children: [
10789
11244
  /* @__PURE__ */ jsx5("strong", { children: room.name }),
10790
- remaining !== null ? ` \xB7 ${clock(remaining)} left` : "",
11245
+ remaining !== null && closing === null ? ` \xB7 ${clock(remaining)} left` : "",
10791
11246
  /* @__PURE__ */ jsx5("button", { type: "button", className: "mx-meet__chip", onClick: () => void moveTo(null), children: "Return to main room" })
10792
- ] }) : assigned !== null ? /* @__PURE__ */ jsxs2("span", { children: [
10793
- "You are assigned to ",
10794
- /* @__PURE__ */ jsx5("strong", { children: assigned.name }),
10795
- remaining !== null ? ` \xB7 ${clock(remaining)} left` : "",
10796
- /* @__PURE__ */ jsx5(
10797
- "button",
10798
- {
10799
- type: "button",
10800
- className: "mx-meet__primary mx-meet__primary--small",
10801
- onClick: () => void act({ action: "breakout_join", roomId: assigned.id }).then(async (said) => {
10802
- const result = said.ok ? await moveTo(assigned.id) : said;
10803
- setFailure(result.ok ? null : result.message);
10804
- }),
10805
- children: "Join"
10806
- }
10807
- )
10808
- ] }) : open?.allowChoose ? /* @__PURE__ */ jsx5("span", { children: "Breakout rooms are open \u2014 pick one under Activities." }) : null,
10809
- showBroadcast ? /* @__PURE__ */ jsxs2("span", { className: "mx-meet__breakout-broadcast", children: [
10810
- /* @__PURE__ */ jsxs2("strong", { children: [
11247
+ ] }) : assigned !== null && later !== open?.id ? /* @__PURE__ */ jsxs3("span", { children: [
11248
+ autoLeft !== null ? `You're being moved to ${assigned.name} in ${Math.ceil(autoLeft / 1e3)} s` : `You're invited to ${assigned.name}`,
11249
+ remaining !== null && closing === null ? ` \xB7 ${clock(remaining)} left` : "",
11250
+ /* @__PURE__ */ jsx5("button", { type: "button", className: "mx-meet__primary mx-meet__primary--small", onClick: () => void joinAssigned(), children: "Join room" }),
11251
+ /* @__PURE__ */ jsx5("button", { type: "button", className: "mx-meet__chip", onClick: () => setLater(open?.id ?? null), children: "Later" })
11252
+ ] }) : assigned !== null ? /* @__PURE__ */ jsxs3("span", { children: [
11253
+ assigned.name,
11254
+ " is open",
11255
+ /* @__PURE__ */ jsx5("button", { type: "button", className: "mx-meet__chip", onClick: () => void joinAssigned(), children: "Join room" })
11256
+ ] }) : open?.allowChoose ? /* @__PURE__ */ jsx5("span", { children: "Rooms are open \u2014 pick one under Activities." }) : null,
11257
+ showBroadcast ? /* @__PURE__ */ jsxs3("span", { className: "mx-meet__breakout-broadcast", children: [
11258
+ /* @__PURE__ */ jsxs3("strong", { children: [
10811
11259
  broadcast.by,
10812
11260
  ":"
10813
11261
  ] }),
@@ -10827,10 +11275,10 @@ function CaptionLanguageSettings() {
10827
11275
  const preferred = defaultCaptionLanguage(settings, viewer);
10828
11276
  const current = collab?.captionLanguage ?? "";
10829
11277
  const choices = CAPTION_LANGUAGES.some((l) => l.tag === preferred) ? CAPTION_LANGUAGES : [{ tag: preferred, name: preferred }, ...CAPTION_LANGUAGES];
10830
- return /* @__PURE__ */ jsxs2("section", { className: "mx-meet__section", "aria-label": "Captions", children: [
10831
- /* @__PURE__ */ jsxs2("label", { className: "mx-meet__field", children: [
11278
+ return /* @__PURE__ */ jsxs3("section", { className: "mx-meet__section", "aria-label": "Captions", children: [
11279
+ /* @__PURE__ */ jsxs3("label", { className: "mx-meet__field", children: [
10832
11280
  /* @__PURE__ */ jsx5("span", { className: "mx-meet__field-label", children: "Translate captions to" }),
10833
- /* @__PURE__ */ jsxs2(
11281
+ /* @__PURE__ */ jsxs3(
10834
11282
  "select",
10835
11283
  {
10836
11284
  className: "mx-meet__select",
@@ -10841,7 +11289,7 @@ function CaptionLanguageSettings() {
10841
11289
  },
10842
11290
  children: [
10843
11291
  /* @__PURE__ */ jsx5("option", { value: "", children: "Off \u2014 as spoken" }),
10844
- current === "" ? /* @__PURE__ */ jsxs2("option", { value: preferred, children: [
11292
+ current === "" ? /* @__PURE__ */ jsxs3("option", { value: preferred, children: [
10845
11293
  choices.find((c) => c.tag === preferred)?.name ?? preferred,
10846
11294
  " (yours)"
10847
11295
  ] }) : null,
@@ -10853,12 +11301,39 @@ function CaptionLanguageSettings() {
10853
11301
  collab?.captionTranslationError != null ? /* @__PURE__ */ jsx5("p", { className: "mx-meet__panel-note mx-meet__collab-error", children: collab.captionTranslationError }) : null
10854
11302
  ] });
10855
11303
  }
11304
+ function CaptionsControl() {
11305
+ const captions = useCaptions();
11306
+ return /* @__PURE__ */ jsx5(
11307
+ ControlButton,
11308
+ {
11309
+ label: captions.on ? "Turn off captions" : "Turn on captions",
11310
+ active: captions.on,
11311
+ pressed: captions.on,
11312
+ onClick: captions.toggle,
11313
+ children: /* @__PURE__ */ jsx5(CaptionsIcon, {})
11314
+ }
11315
+ );
11316
+ }
11317
+ function TakeNotesControl() {
11318
+ const ai = useMeetAi();
11319
+ if (!ai.canControlNoteTaker) return null;
11320
+ const running = ai.noteTaker.state === "joining" || ai.noteTaker.state === "transcribing";
11321
+ return /* @__PURE__ */ jsx5(
11322
+ ControlButton,
11323
+ {
11324
+ label: running ? "Stop taking notes" : "Take notes for me",
11325
+ active: running,
11326
+ onClick: () => void (running ? ai.stopNoteTaker() : ai.startNoteTaker()),
11327
+ children: /* @__PURE__ */ jsx5(NotesIcon, {})
11328
+ }
11329
+ );
11330
+ }
10856
11331
 
10857
11332
  // src/skins/meet/components.tsx
10858
- import { Fragment, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
11333
+ import { Fragment, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
10859
11334
  function InertNotice({ what }) {
10860
- return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__empty", role: "status", children: [
10861
- /* @__PURE__ */ jsxs3("p", { className: "mx-meet__empty-title", children: [
11335
+ return /* @__PURE__ */ jsxs4("div", { className: "mx-meet__empty", role: "status", children: [
11336
+ /* @__PURE__ */ jsxs4("p", { className: "mx-meet__empty-title", children: [
10862
11337
  what,
10863
11338
  " is not ready yet"
10864
11339
  ] }),
@@ -10903,6 +11378,7 @@ function ControlButton({
10903
11378
  label,
10904
11379
  active = false,
10905
11380
  danger = false,
11381
+ pressed,
10906
11382
  onClick,
10907
11383
  children
10908
11384
  }) {
@@ -10917,26 +11393,49 @@ function ControlButton({
10917
11393
  ].filter(Boolean).join(" "),
10918
11394
  "aria-label": label,
10919
11395
  title: label,
11396
+ ...pressed !== void 0 ? { "aria-pressed": pressed } : {},
10920
11397
  onClick,
10921
11398
  children
10922
11399
  }
10923
11400
  );
10924
11401
  }
10925
11402
  function RecordingIndicator() {
10926
- const { state, label } = useRecording();
10927
- if (label === null) return null;
10928
- return /* @__PURE__ */ jsxs3(
10929
- "span",
10930
- {
10931
- className: `mx-meet__recording mx-meet__recording--${state}`,
10932
- role: "status",
10933
- "aria-live": "polite",
10934
- children: [
10935
- /* @__PURE__ */ jsx6(RecordIcon, { width: 10, height: 10 }),
10936
- label
10937
- ]
10938
- }
10939
- );
11403
+ const { state, label, canStart } = useRecording();
11404
+ const snapshot = useMeetSnapshot();
11405
+ const previous = useRef6(state);
11406
+ const [stoppedAt, setStoppedAt] = useState5(null);
11407
+ useEffect6(() => {
11408
+ const was = previous.current;
11409
+ previous.current = state;
11410
+ if ((was === "recording" || was === "starting") && state !== "recording" && state !== "starting") {
11411
+ setStoppedAt(Date.now());
11412
+ const timer = setTimeout(() => setStoppedAt(null), 1e4);
11413
+ return () => clearTimeout(timer);
11414
+ }
11415
+ return void 0;
11416
+ }, [state]);
11417
+ const meetingId = snapshot?.meeting?.id ?? null;
11418
+ if (label === null && stoppedAt === null) return null;
11419
+ return /* @__PURE__ */ jsxs4(Fragment, { children: [
11420
+ label !== null ? /* @__PURE__ */ jsxs4(
11421
+ "span",
11422
+ {
11423
+ className: `mx-meet__recording mx-meet__recording--${state}`,
11424
+ role: "status",
11425
+ "aria-live": "polite",
11426
+ children: [
11427
+ /* @__PURE__ */ jsx6(RecordIcon, { width: 10, height: 10 }),
11428
+ label
11429
+ ]
11430
+ }
11431
+ ) : null,
11432
+ stoppedAt !== null ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__recording", role: "status", "data-meet-notice": "recording-stopped", children: "Recording stopped" }) : null,
11433
+ state === "processing" ? (
11434
+ // rec-processing: the host is told it is being made ready (the indicator says Processing…).
11435
+ /* @__PURE__ */ jsx6("span", { className: "mx-meet__recording", role: "status", "data-meet-notice": "recording-processing", children: "Your recording is being processed. A link appears here when it is ready." })
11436
+ ) : null,
11437
+ state === "available" && canStart && meetingId !== null ? /* @__PURE__ */ jsx6("a", { className: "mx-meet__link", href: `/meetings/${meetingId}`, target: "_blank", rel: "noreferrer", children: "View recording" }) : null
11438
+ ] });
10940
11439
  }
10941
11440
  function RoleBadge({
10942
11441
  role,
@@ -10955,7 +11454,7 @@ function ParticipantTile({
10955
11454
  const host = useMeetHost();
10956
11455
  const snapshot = useMeetSnapshot();
10957
11456
  const queue = useHandQueue();
10958
- const [menu, setMenu] = useState4(false);
11457
+ const [menu, setMenu] = useState5(false);
10959
11458
  const videoRef = useTrack(
10960
11459
  participant.identity,
10961
11460
  participant.screenSharing ? "screen" : "camera"
@@ -10965,7 +11464,7 @@ function ParticipantTile({
10965
11464
  const spotlit = snapshot?.spotlight === participant.identity;
10966
11465
  const presenting = snapshot?.presenter === participant.identity;
10967
11466
  const handPosition = participant.handRaised ? queue.findIndex((entry) => entry.identity === participant.identity) + 1 : 0;
10968
- return /* @__PURE__ */ jsxs3(
11467
+ return /* @__PURE__ */ jsxs4(
10969
11468
  "div",
10970
11469
  {
10971
11470
  className: [
@@ -10988,12 +11487,12 @@ function ParticipantTile({
10988
11487
  }
10989
11488
  ) : /* @__PURE__ */ jsx6("div", { className: "mx-meet__tile-placeholder", children: /* @__PURE__ */ jsx6(Avatar, { name: participant.displayName, url: participant.avatarUrl, size: featured ? 96 : 56 }) }),
10990
11489
  presenting ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__tile-presenting", role: "status", children: participant.isLocal ? "You are presenting" : `${participant.displayName} is presenting` }) : null,
10991
- participant.handRaised ? /* @__PURE__ */ jsxs3("span", { className: "mx-meet__tile-hand", title: `Hand raised \xB7 ${ordinal(handPosition)} in line`, children: [
11490
+ participant.handRaised ? /* @__PURE__ */ jsxs4("span", { className: "mx-meet__tile-hand", title: `Hand raised \xB7 ${ordinal(handPosition)} in line`, children: [
10992
11491
  /* @__PURE__ */ jsx6(HandIcon, { width: 14, height: 14 }),
10993
11492
  handPosition > 0 ? handPosition : null
10994
11493
  ] }) : null,
10995
- /* @__PURE__ */ jsxs3("div", { className: "mx-meet__tile-bar", children: [
10996
- /* @__PURE__ */ jsxs3("span", { className: "mx-meet__tile-name", children: [
11494
+ /* @__PURE__ */ jsxs4("div", { className: "mx-meet__tile-bar", children: [
11495
+ /* @__PURE__ */ jsxs4("span", { className: "mx-meet__tile-name", children: [
10997
11496
  spotlit ? /* @__PURE__ */ jsx6("span", { title: "Spotlighted for everyone", className: "mx-meet__tile-flag", children: /* @__PURE__ */ jsx6(SpotlightIcon, { width: 12, height: 12 }) }) : null,
10998
11497
  participant.displayName,
10999
11498
  participant.isLocal ? " (you)" : "",
@@ -11004,7 +11503,7 @@ function ParticipantTile({
11004
11503
  !participant.micEnabled ? /* @__PURE__ */ jsx6(MicOffIcon, { width: 14, height: 14 }) : null,
11005
11504
  /* @__PURE__ */ jsx6(QualityBars, { quality: participant.quality })
11006
11505
  ] }),
11007
- /* @__PURE__ */ jsxs3("div", { className: menu ? "mx-meet__tile-actions mx-meet__tile-actions--open" : "mx-meet__tile-actions", children: [
11506
+ /* @__PURE__ */ jsxs4("div", { className: menu ? "mx-meet__tile-actions mx-meet__tile-actions--open" : "mx-meet__tile-actions", children: [
11008
11507
  /* @__PURE__ */ jsx6(
11009
11508
  ControlButton,
11010
11509
  {
@@ -11014,7 +11513,7 @@ function ParticipantTile({
11014
11513
  children: /* @__PURE__ */ jsx6(PinIcon, { width: 16, height: 16 })
11015
11514
  }
11016
11515
  ),
11017
- /* @__PURE__ */ jsxs3("span", { className: "mx-meet__anchor", children: [
11516
+ /* @__PURE__ */ jsxs4("span", { className: "mx-meet__anchor", children: [
11018
11517
  /* @__PURE__ */ jsx6(
11019
11518
  ControlButton,
11020
11519
  {
@@ -11052,12 +11551,12 @@ function Stage({ pageSize }) {
11052
11551
  const stage = useStage(pageSize);
11053
11552
  const columns = gridColumns(stage.tiles.length);
11054
11553
  if (stage.feature === null && stage.tiles.length === 0) {
11055
- return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__stage mx-meet__stage--empty", children: [
11554
+ return /* @__PURE__ */ jsxs4("div", { className: "mx-meet__stage mx-meet__stage--empty", children: [
11056
11555
  /* @__PURE__ */ jsx6("p", { className: "mx-meet__empty-title", children: "Waiting for others to join" }),
11057
11556
  /* @__PURE__ */ jsx6("p", { className: "mx-meet__empty-body", children: "Share the meeting link and people can join from anywhere \u2014 no account needed." })
11058
11557
  ] });
11059
11558
  }
11060
- return /* @__PURE__ */ jsxs3("div", { className: `mx-meet__stage${stage.feature !== null ? " mx-meet__stage--speaker" : ""}`, children: [
11559
+ return /* @__PURE__ */ jsxs4("div", { className: `mx-meet__stage${stage.feature !== null ? " mx-meet__stage--speaker" : ""}`, children: [
11061
11560
  stage.feature !== null ? /* @__PURE__ */ jsx6(ParticipantTile, { participant: stage.feature, featured: true }) : null,
11062
11561
  /* @__PURE__ */ jsx6(
11063
11562
  "div",
@@ -11067,7 +11566,7 @@ function Stage({ pageSize }) {
11067
11566
  children: stage.tiles.map((participant) => /* @__PURE__ */ jsx6(ParticipantTile, { participant }, participant.identity))
11068
11567
  }
11069
11568
  ),
11070
- stage.overflow > 0 ? /* @__PURE__ */ jsxs3("p", { className: "mx-meet__overflow", children: [
11569
+ stage.overflow > 0 ? /* @__PURE__ */ jsxs4("p", { className: "mx-meet__overflow", children: [
11071
11570
  "+",
11072
11571
  stage.overflow,
11073
11572
  " more ",
@@ -11079,17 +11578,18 @@ function Stage({ pageSize }) {
11079
11578
  function ControlBar({ onLeave }) {
11080
11579
  const room = useRoom();
11081
11580
  const recording = useRecording();
11082
- const [panel, setPanel] = useState4("none");
11083
- const [chatTo, setChatTo] = useState4(null);
11084
- const [popover, setPopover] = useState4("none");
11085
- const [shortcuts, setShortcuts] = useState4(false);
11581
+ const [panel, setPanel] = useState5("none");
11582
+ const [chatTo, setChatTo] = useState5(null);
11583
+ const [popover, setPopover] = useState5("none");
11584
+ const [shortcuts, setShortcuts] = useState5(false);
11086
11585
  const snapshot = useMeetSnapshot();
11087
11586
  const lobby = useLobby();
11088
11587
  const controls = useHostControls();
11089
11588
  const queue = useHandQueue();
11090
11589
  const micUnavailable = useLocalMedia().microphone.unavailable;
11590
+ const live = useLivePolicy();
11091
11591
  const unread = useUnreadChat(panel === "chat");
11092
- const [whiteboard, setWhiteboard] = useState4(false);
11592
+ const [whiteboard, setWhiteboard] = useState5(false);
11093
11593
  const app = useMeetAppPanel();
11094
11594
  const appOpened = useRef6(false);
11095
11595
  useEffect6(() => {
@@ -11123,7 +11623,7 @@ function ControlBar({ onLeave }) {
11123
11623
  help: () => setShortcuts(true)
11124
11624
  });
11125
11625
  const hasSpotlight = snapshot?.spotlight !== null && snapshot?.spotlight !== void 0;
11126
- return /* @__PURE__ */ jsxs3(Fragment, { children: [
11626
+ return /* @__PURE__ */ jsxs4(Fragment, { children: [
11127
11627
  /* @__PURE__ */ jsx6(MeetingOverlays, {}),
11128
11628
  shortcuts ? /* @__PURE__ */ jsx6(ShortcutsSheet, { onClose: () => setShortcuts(false) }) : null,
11129
11629
  panel === "chat" ? /* @__PURE__ */ jsx6(ChatPanel, { onClose: () => setPanel("none"), to: chatTo, onTo: setChatTo }) : null,
@@ -11153,9 +11653,10 @@ function ControlBar({ onLeave }) {
11153
11653
  ) }) }) : null,
11154
11654
  /* @__PURE__ */ jsx6(BreakoutBanner, {}),
11155
11655
  panel !== "people" ? /* @__PURE__ */ jsx6(KnockNotice, { onView: () => setPanel("people") }) : null,
11656
+ /* @__PURE__ */ jsx6(SpeakingWhileMutedNudge, {}),
11156
11657
  controls.notice !== null && panel !== "people" ? /* @__PURE__ */ jsx6(Toast, { text: controls.notice.text, failed: controls.notice.failed, onDone: controls.clearNotice }) : null,
11157
- /* @__PURE__ */ jsxs3("div", { className: "mx-meet__controls", children: [
11158
- /* @__PURE__ */ jsx6(
11658
+ /* @__PURE__ */ jsxs4("div", { className: "mx-meet__controls", children: [
11659
+ live.micLocked ? null : /* @__PURE__ */ jsx6(
11159
11660
  ControlButton,
11160
11661
  {
11161
11662
  label: micUnavailable !== null ? `Unmute \u2014 ${micUnavailable.remedy}` : room.local?.micEnabled === true ? "Mute (\u2318D)" : "Unmute (\u2318D)",
@@ -11164,7 +11665,8 @@ function ControlBar({ onLeave }) {
11164
11665
  children: room.local?.micEnabled === true ? /* @__PURE__ */ jsx6(MicIcon, {}) : /* @__PURE__ */ jsx6(MicOffIcon, {})
11165
11666
  }
11166
11667
  ),
11167
- /* @__PURE__ */ jsx6(
11668
+ /* @__PURE__ */ jsx6(AudioSettingsControl, {}),
11669
+ live.cameraLocked ? null : /* @__PURE__ */ jsx6(
11168
11670
  ControlButton,
11169
11671
  {
11170
11672
  label: room.local?.cameraEnabled === true ? "Stop video (\u2318E)" : "Start video (\u2318E)",
@@ -11173,6 +11675,7 @@ function ControlBar({ onLeave }) {
11173
11675
  children: room.local?.cameraEnabled === true ? /* @__PURE__ */ jsx6(CameraIcon, {}) : /* @__PURE__ */ jsx6(CameraOffIcon, {})
11174
11676
  }
11175
11677
  ),
11678
+ /* @__PURE__ */ jsx6(CaptionsControl, {}),
11176
11679
  room.canShareScreen || room.local?.screenSharing === true ? /* @__PURE__ */ jsx6(
11177
11680
  ControlButton,
11178
11681
  {
@@ -11182,7 +11685,7 @@ function ControlBar({ onLeave }) {
11182
11685
  children: /* @__PURE__ */ jsx6(ScreenShareIcon, {})
11183
11686
  }
11184
11687
  ) : null,
11185
- /* @__PURE__ */ jsxs3("span", { className: "mx-meet__anchor", children: [
11688
+ /* @__PURE__ */ jsxs4("span", { className: "mx-meet__anchor", children: [
11186
11689
  /* @__PURE__ */ jsx6(
11187
11690
  ControlButton,
11188
11691
  {
@@ -11214,11 +11717,11 @@ function ControlBar({ onLeave }) {
11214
11717
  }
11215
11718
  ),
11216
11719
  app !== null ? /* @__PURE__ */ jsx6(ControlButton, { label: app.title, active: panel === "app", onClick: () => toggle("app"), children: /* @__PURE__ */ jsx6(FlagIcon, {}) }) : null,
11217
- /* @__PURE__ */ jsxs3(ControlButton, { label: "Chat (\u2318\u2325C)", active: panel === "chat", onClick: () => toggle("chat"), children: [
11720
+ /* @__PURE__ */ jsxs4(ControlButton, { label: "Chat (\u2318\u2325C)", active: panel === "chat", onClick: () => toggle("chat"), children: [
11218
11721
  /* @__PURE__ */ jsx6(ChatIcon, {}),
11219
11722
  unread > 0 ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__dot", "aria-hidden": "true" }) : null
11220
11723
  ] }),
11221
- /* @__PURE__ */ jsxs3(
11724
+ /* @__PURE__ */ jsxs4(
11222
11725
  ControlButton,
11223
11726
  {
11224
11727
  label: lobby.waiting.length > 0 ? `People \u2014 ${lobby.waiting.length} waiting` : `People \u2014 ${(snapshot?.participants ?? []).length} in the meeting`,
@@ -11237,10 +11740,11 @@ function ControlBar({ onLeave }) {
11237
11740
  onOpen: openActivity
11238
11741
  }
11239
11742
  ),
11743
+ /* @__PURE__ */ jsx6(TakeNotesControl, {}),
11240
11744
  /* @__PURE__ */ jsx6(AiControl, { active: panel === "ai", onToggle: () => toggle("ai") }),
11241
11745
  recording.canStart ? /* @__PURE__ */ jsx6(ControlButton, { label: "Start recording", onClick: () => void recording.start(), children: /* @__PURE__ */ jsx6(RecordIcon, {}) }) : null,
11242
11746
  recording.canStop ? /* @__PURE__ */ jsx6(ControlButton, { label: "Stop recording", active: true, onClick: () => void recording.stop(), children: /* @__PURE__ */ jsx6(RecordIcon, {}) }) : null,
11243
- /* @__PURE__ */ jsxs3("span", { className: "mx-meet__anchor", children: [
11747
+ /* @__PURE__ */ jsxs4("span", { className: "mx-meet__anchor", children: [
11244
11748
  /* @__PURE__ */ jsx6(
11245
11749
  ControlButton,
11246
11750
  {
@@ -11250,8 +11754,8 @@ function ControlBar({ onLeave }) {
11250
11754
  children: /* @__PURE__ */ jsx6(MoreIcon, {})
11251
11755
  }
11252
11756
  ),
11253
- popover === "more" ? /* @__PURE__ */ jsx6(Popover, { label: "More options", onClose: () => setPopover("none"), children: /* @__PURE__ */ jsxs3("div", { className: "mx-meet__menu", role: "menu", children: [
11254
- /* @__PURE__ */ jsxs3(
11757
+ popover === "more" ? /* @__PURE__ */ jsx6(Popover, { label: "More options", onClose: () => setPopover("none"), children: /* @__PURE__ */ jsxs4("div", { className: "mx-meet__menu", role: "menu", children: [
11758
+ /* @__PURE__ */ jsxs4(
11255
11759
  MenuItem,
11256
11760
  {
11257
11761
  onSelect: () => {
@@ -11264,7 +11768,7 @@ function ControlBar({ onLeave }) {
11264
11768
  ]
11265
11769
  }
11266
11770
  ),
11267
- /* @__PURE__ */ jsxs3(
11771
+ /* @__PURE__ */ jsxs4(
11268
11772
  MenuItem,
11269
11773
  {
11270
11774
  onSelect: () => {
@@ -11277,9 +11781,16 @@ function ControlBar({ onLeave }) {
11277
11781
  ]
11278
11782
  }
11279
11783
  ),
11280
- controls.isHost ? /* @__PURE__ */ jsxs3(Fragment, { children: [
11784
+ !room.canShareScreen && room.local?.screenSharing !== true ? (
11785
+ // `share-unsupported-mobile`: the control is absent; the menu says where it lives.
11786
+ /* @__PURE__ */ jsxs4("p", { className: "mx-meet__menu-note", role: "note", "data-meet-notice": "share-unsupported", children: [
11787
+ /* @__PURE__ */ jsx6(ScreenShareIcon, { width: 16, height: 16 }),
11788
+ " Present from a computer"
11789
+ ] })
11790
+ ) : null,
11791
+ controls.isHost ? /* @__PURE__ */ jsxs4(Fragment, { children: [
11281
11792
  /* @__PURE__ */ jsx6("div", { className: "mx-meet__menu-sep", role: "separator" }),
11282
- /* @__PURE__ */ jsxs3(
11793
+ /* @__PURE__ */ jsxs4(
11283
11794
  MenuItem,
11284
11795
  {
11285
11796
  onSelect: () => {
@@ -11292,7 +11803,24 @@ function ControlBar({ onLeave }) {
11292
11803
  ]
11293
11804
  }
11294
11805
  ),
11295
- /* @__PURE__ */ jsxs3(
11806
+ /* @__PURE__ */ jsxs4(
11807
+ MenuItem,
11808
+ {
11809
+ onSelect: () => {
11810
+ setPopover("none");
11811
+ void controls.run({
11812
+ action: "set_av_lock",
11813
+ value: snapshot?.livePolicy.avLock === "both" ? "off" : "both"
11814
+ });
11815
+ },
11816
+ children: [
11817
+ /* @__PURE__ */ jsx6(MicOffIcon, { width: 16, height: 16 }),
11818
+ " ",
11819
+ snapshot?.livePolicy.avLock === "both" ? "Turn on attendees' microphone and camera" : "Turn off attendees' microphone and camera"
11820
+ ]
11821
+ }
11822
+ ),
11823
+ /* @__PURE__ */ jsxs4(
11296
11824
  MenuItem,
11297
11825
  {
11298
11826
  onSelect: () => {
@@ -11305,7 +11833,7 @@ function ControlBar({ onLeave }) {
11305
11833
  ]
11306
11834
  }
11307
11835
  ),
11308
- queue.length > 0 ? /* @__PURE__ */ jsxs3(
11836
+ queue.length > 0 ? /* @__PURE__ */ jsxs4(
11309
11837
  MenuItem,
11310
11838
  {
11311
11839
  onSelect: () => {
@@ -11320,7 +11848,7 @@ function ControlBar({ onLeave }) {
11320
11848
  ]
11321
11849
  }
11322
11850
  ) : null,
11323
- hasSpotlight ? /* @__PURE__ */ jsxs3(
11851
+ hasSpotlight ? /* @__PURE__ */ jsxs4(
11324
11852
  MenuItem,
11325
11853
  {
11326
11854
  onSelect: () => {
@@ -11347,8 +11875,8 @@ var LEAVE_WORDS = {
11347
11875
  };
11348
11876
  function LeaveControl({ onLeave }) {
11349
11877
  const leave = useLeave();
11350
- const [open, setOpen] = useState4("none");
11351
- const [failure, setFailure] = useState4(null);
11878
+ const [open, setOpen] = useState5("none");
11879
+ const [failure, setFailure] = useState5(null);
11352
11880
  const words = LEAVE_WORDS[leave.policy.hostLeavePrompt];
11353
11881
  const run = (work, leaving) => {
11354
11882
  setFailure(null);
@@ -11359,7 +11887,7 @@ function LeaveControl({ onLeave }) {
11359
11887
  };
11360
11888
  const leaveNow = () => run(leave.leave, true);
11361
11889
  const endNow = () => run(leave.endForAll, false);
11362
- return /* @__PURE__ */ jsxs3("span", { className: "mx-meet__anchor", children: [
11890
+ return /* @__PURE__ */ jsxs4("span", { className: "mx-meet__anchor", children: [
11363
11891
  /* @__PURE__ */ jsx6(
11364
11892
  ControlButton,
11365
11893
  {
@@ -11373,7 +11901,7 @@ function LeaveControl({ onLeave }) {
11373
11901
  children: /* @__PURE__ */ jsx6(PhoneOffIcon, {})
11374
11902
  }
11375
11903
  ),
11376
- open !== "none" ? /* @__PURE__ */ jsx6(Popover, { label: open === "assign" ? words.assign_and_leave : "Leave", onClose: () => setOpen("none"), children: /* @__PURE__ */ jsxs3("div", { className: "mx-meet__menu", "data-meet-leave-menu": leave.policy.hostLeavePrompt, children: [
11904
+ open !== "none" ? /* @__PURE__ */ jsx6(Popover, { label: open === "assign" ? words.assign_and_leave : "Leave", onClose: () => setOpen("none"), children: /* @__PURE__ */ jsxs4("div", { className: "mx-meet__menu", "data-meet-leave-menu": leave.policy.hostLeavePrompt, children: [
11377
11905
  open === "menu" ? leave.choices.map((choice) => /* @__PURE__ */ jsx6(
11378
11906
  "button",
11379
11907
  {
@@ -11399,7 +11927,7 @@ function LeaveControl({ onLeave }) {
11399
11927
  },
11400
11928
  String(person.identity)
11401
11929
  )) : null,
11402
- open === "confirm" ? /* @__PURE__ */ jsxs3(Fragment, { children: [
11930
+ open === "confirm" ? /* @__PURE__ */ jsxs4(Fragment, { children: [
11403
11931
  /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__menu-item mx-meet__menu-item--danger", onClick: endNow, children: words.end_for_all }),
11404
11932
  /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__menu-item", onClick: () => setOpen("menu"), children: "Cancel" })
11405
11933
  ] }) : null,
@@ -11420,12 +11948,12 @@ function useUnreadChat(open) {
11420
11948
  const snapshot = useMeetSnapshot();
11421
11949
  const chat = snapshot?.chat ?? [];
11422
11950
  const local = snapshot?.localIdentity ?? null;
11423
- const [seen, setSeen] = useState4(0);
11951
+ const [seen, setSeen] = useState5(0);
11424
11952
  useEffect6(() => {
11425
11953
  if (open) setSeen(chat.length);
11426
11954
  }, [open, chat.length]);
11427
11955
  const loaded = snapshot?.chatLoaded === true;
11428
- const [baseline, setBaseline] = useState4(null);
11956
+ const [baseline, setBaseline] = useState5(null);
11429
11957
  useEffect6(() => {
11430
11958
  if (loaded && baseline === null) setBaseline(chat.length);
11431
11959
  }, [loaded, baseline, chat.length]);
@@ -11440,7 +11968,7 @@ function Popover({
11440
11968
  placement = "up"
11441
11969
  }) {
11442
11970
  const ref = useRef6(null);
11443
- const [style, setStyle] = useState4({
11971
+ const [style, setStyle] = useState5({
11444
11972
  position: "fixed",
11445
11973
  top: 0,
11446
11974
  left: 0,
@@ -11529,15 +12057,103 @@ function Toast({ text: text3, failed, onDone }) {
11529
12057
  const timer = setTimeout(onDone, failed ? 7e3 : 4e3);
11530
12058
  return () => clearTimeout(timer);
11531
12059
  }, [text3, failed, onDone]);
11532
- return /* @__PURE__ */ jsxs3("div", { className: failed ? "mx-meet__toast mx-meet__toast--failed" : "mx-meet__toast", role: "status", children: [
12060
+ return /* @__PURE__ */ jsxs4("div", { className: failed ? "mx-meet__toast mx-meet__toast--failed" : "mx-meet__toast", role: "status", children: [
11533
12061
  /* @__PURE__ */ jsx6("span", { children: text3 }),
11534
12062
  /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__toast-close", "aria-label": "Dismiss", onClick: onDone, children: /* @__PURE__ */ jsx6(CloseIcon, { width: 14, height: 14 }) })
11535
12063
  ] });
11536
12064
  }
12065
+ function AudioSettingsControl() {
12066
+ const host = useMeetHost();
12067
+ const devices = useDeviceSnapshot();
12068
+ const [open, setOpen] = useState5(false);
12069
+ const list = (devices?.devices.audioinput ?? []).filter((device) => device.deviceId.length > 0);
12070
+ if (host === null || list.length < 2) return null;
12071
+ const current = devices?.selected.audioinput ?? host.devices.resolve("audioinput");
12072
+ return /* @__PURE__ */ jsxs4("span", { className: "mx-meet__anchor", children: [
12073
+ /* @__PURE__ */ jsx6(
12074
+ ControlButton,
12075
+ {
12076
+ label: "Audio settings",
12077
+ active: open,
12078
+ onClick: () => {
12079
+ if (!open) void host.devices.refresh();
12080
+ setOpen(!open);
12081
+ },
12082
+ children: /* @__PURE__ */ jsx6(ChevronUpIcon, {})
12083
+ }
12084
+ ),
12085
+ open ? /* @__PURE__ */ jsx6(Popover, { label: "Microphone", onClose: () => setOpen(false), children: /* @__PURE__ */ jsx6("div", { className: "mx-meet__menu", role: "menu", "aria-label": "Microphone", children: list.map((device) => /* @__PURE__ */ jsxs4(
12086
+ "button",
12087
+ {
12088
+ type: "button",
12089
+ role: "menuitemradio",
12090
+ "aria-checked": device.deviceId === current,
12091
+ className: "mx-meet__menu-item",
12092
+ onClick: () => {
12093
+ setOpen(false);
12094
+ host.devices.select("audioinput", device.deviceId);
12095
+ void host.engine.switchDevice("audioinput", device.deviceId).catch(() => void 0);
12096
+ },
12097
+ children: [
12098
+ device.deviceId === current ? /* @__PURE__ */ jsx6(CheckIcon, { width: 16, height: 16 }) : /* @__PURE__ */ jsx6("span", { className: "mx-meet__menu-gap" }),
12099
+ device.label.length > 0 ? device.label : "Microphone"
12100
+ ]
12101
+ },
12102
+ device.deviceId
12103
+ )) }) }) : null
12104
+ ] });
12105
+ }
12106
+ function SpeakingWhileMutedNudge() {
12107
+ const room = useRoom();
12108
+ const host = useMeetHost();
12109
+ const devices = useDeviceSnapshot();
12110
+ const microphone = useLocalMedia().microphone;
12111
+ const mutedByMe = host !== null && room.local != null && room.local.micEnabled !== true && microphone.unavailable === null;
12112
+ const mic = devices?.selected.audioinput ?? host?.devices.resolve("audioinput") ?? null;
12113
+ const stream = useMicrophoneStream(mutedByMe ? mic : "off");
12114
+ const level = useAudioLevel(stream);
12115
+ const latest = useRef6(0);
12116
+ latest.current = level;
12117
+ const [shown, setShown] = useState5(false);
12118
+ const [detector] = useState5(() => createSpeakingWhileMutedDetector());
12119
+ useEffect6(() => {
12120
+ if (!mutedByMe) {
12121
+ detector.reset();
12122
+ setShown(false);
12123
+ return void 0;
12124
+ }
12125
+ const timer = setInterval(() => {
12126
+ if (detector.feed(latest.current, Date.now())) setShown(true);
12127
+ }, 150);
12128
+ return () => clearInterval(timer);
12129
+ }, [mutedByMe, detector]);
12130
+ useEffect6(() => {
12131
+ if (!shown) return void 0;
12132
+ const timer = setTimeout(() => setShown(false), 8e3);
12133
+ return () => clearTimeout(timer);
12134
+ }, [shown]);
12135
+ if (!shown || !mutedByMe) return null;
12136
+ return /* @__PURE__ */ jsxs4("div", { className: "mx-meet__notice-bar", role: "status", "data-meet-notice": "speaking-while-muted", children: [
12137
+ /* @__PURE__ */ jsx6(MicOffIcon, { width: 16, height: 16 }),
12138
+ /* @__PURE__ */ jsx6("span", { children: "You're muted" }),
12139
+ /* @__PURE__ */ jsx6(
12140
+ "button",
12141
+ {
12142
+ type: "button",
12143
+ className: "mx-meet__primary mx-meet__primary--small",
12144
+ onClick: () => {
12145
+ setShown(false);
12146
+ void room.toggleMicrophone();
12147
+ },
12148
+ children: "Unmute"
12149
+ }
12150
+ )
12151
+ ] });
12152
+ }
11537
12153
  function MediaNotices() {
11538
12154
  const media = useLocalMedia();
11539
12155
  const { region, where, enableAudio, acceptOffer, dismissOffer, dismissNotice, retry } = useMediaSupervisor();
11540
- const [fixing, setFixing] = useState4(null);
12156
+ const [fixing, setFixing] = useState5(null);
11541
12157
  const chips = ["microphone", "camera"].flatMap((kind) => {
11542
12158
  const reason = deviceUnavailableReason(media[kind], region.permission[kind]);
11543
12159
  return reason === null ? [] : [{ kind, reason }];
@@ -11547,8 +12163,8 @@ function MediaNotices() {
11547
12163
  const offer = region.offer;
11548
12164
  const name = (kind) => kind === "microphone" ? "Microphone" : "Camera";
11549
12165
  if (chips.length === 0 && region.audioOutput !== "blocked" && notice === null && offer === null) return null;
11550
- return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__media-notices", children: [
11551
- region.audioOutput === "blocked" ? /* @__PURE__ */ jsxs3(
12166
+ return /* @__PURE__ */ jsxs4("div", { className: "mx-meet__media-notices", children: [
12167
+ region.audioOutput === "blocked" ? /* @__PURE__ */ jsxs4(
11552
12168
  ErrorBox,
11553
12169
  {
11554
12170
  as: "div",
@@ -11563,7 +12179,7 @@ function MediaNotices() {
11563
12179
  ]
11564
12180
  }
11565
12181
  ) : null,
11566
- chips.map(({ kind, reason }) => /* @__PURE__ */ jsxs3("div", { className: "mx-meet__media-pill", role: "status", "data-meet-notice": deviceNoticeCode(kind, reason), children: [
12182
+ chips.map(({ kind, reason }) => /* @__PURE__ */ jsxs4("div", { className: "mx-meet__media-pill", role: "status", "data-meet-notice": deviceNoticeCode(kind, reason), children: [
11567
12183
  /* @__PURE__ */ jsx6("span", { children: deviceChipLabel(kind, reason) }),
11568
12184
  /* @__PURE__ */ jsx6(
11569
12185
  "button",
@@ -11577,9 +12193,9 @@ function MediaNotices() {
11577
12193
  }
11578
12194
  )
11579
12195
  ] }, kind)),
11580
- fixing !== null && fixReason !== null ? /* @__PURE__ */ jsxs3("div", { className: "mx-meet__popover mx-meet__fix-sheet", role: "dialog", "aria-label": `Fix ${name(fixing).toLowerCase()}`, children: [
12196
+ fixing !== null && fixReason !== null ? /* @__PURE__ */ jsxs4("div", { className: "mx-meet__popover mx-meet__fix-sheet", role: "dialog", "aria-label": `Fix ${name(fixing).toLowerCase()}`, children: [
11581
12197
  /* @__PURE__ */ jsx6("ol", { className: "mx-meet__fix-steps", children: deviceFixSteps(fixing, fixReason, where).map((step) => /* @__PURE__ */ jsx6("li", { children: step }, step)) }),
11582
- /* @__PURE__ */ jsxs3("div", { className: "mx-meet__fix-actions", children: [
12198
+ /* @__PURE__ */ jsxs4("div", { className: "mx-meet__fix-actions", children: [
11583
12199
  /* @__PURE__ */ jsx6(
11584
12200
  "button",
11585
12201
  {
@@ -11596,7 +12212,7 @@ function MediaNotices() {
11596
12212
  /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__toast-close", "aria-label": "Close", onClick: () => setFixing(null), children: /* @__PURE__ */ jsx6(CloseIcon, { width: 14, height: 14 }) })
11597
12213
  ] })
11598
12214
  ] }) : null,
11599
- notice !== null ? /* @__PURE__ */ jsxs3(
12215
+ notice !== null ? /* @__PURE__ */ jsxs4(
11600
12216
  "div",
11601
12217
  {
11602
12218
  className: "mx-meet__media-pill",
@@ -11608,7 +12224,7 @@ function MediaNotices() {
11608
12224
  ]
11609
12225
  }
11610
12226
  ) : null,
11611
- offer !== null ? /* @__PURE__ */ jsxs3("div", { className: "mx-meet__media-pill", role: "status", children: [
12227
+ offer !== null ? /* @__PURE__ */ jsxs4("div", { className: "mx-meet__media-pill", role: "status", children: [
11612
12228
  /* @__PURE__ */ jsx6("span", { children: `New ${offer.kind}: ${offer.label || name(offer.kind)}` }),
11613
12229
  /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__primary mx-meet__primary--small", onClick: () => void acceptOffer(), children: "Use it" }),
11614
12230
  /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__toast-close", "aria-label": "Dismiss", onClick: dismissOffer, children: /* @__PURE__ */ jsx6(CloseIcon, { width: 14, height: 14 }) })
@@ -11619,14 +12235,50 @@ function MeetingOverlays() {
11619
12235
  const reactions = useReactions();
11620
12236
  const { notice, dismiss } = useModerationNotice();
11621
12237
  const room = useRoom();
11622
- return /* @__PURE__ */ jsxs3(Fragment, { children: [
12238
+ const live = useLivePolicy();
12239
+ const snapshot = useMeetSnapshot();
12240
+ const controls = useHostControls();
12241
+ const isHostNow = useRole() === "host";
12242
+ return /* @__PURE__ */ jsxs4(Fragment, { children: [
11623
12243
  /* @__PURE__ */ jsx6(ReactionOverlay, { reactions }),
11624
12244
  /* @__PURE__ */ jsx6(MediaNotices, {}),
12245
+ snapshot !== null && snapshot.phase === "in_call" && snapshot.participants.length >= 2 && !snapshot.participants.some((p) => p.role === "host" || p.role === "cohost") ? (
12246
+ // meeting-without-host: the call goes on; host-only controls are simply not on screen.
12247
+ /* @__PURE__ */ jsx6("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "host-absent", children: "The host has left the meeting. Host controls are not available until a host is back." })
12248
+ ) : null,
12249
+ live.consent !== null ? /* @__PURE__ */ jsx6("div", { className: "mx-meet__sheet-backdrop", children: /* @__PURE__ */ jsxs4("div", { className: "mx-meet__sheet", role: "alertdialog", "aria-label": "This meeting is being recorded", "data-meet-notice": "recording-consent", children: [
12250
+ /* @__PURE__ */ jsx6("h2", { className: "mx-meet__panel-title", children: "This meeting is being recorded" }),
12251
+ /* @__PURE__ */ jsx6("p", { className: "mx-meet__confirm-text", children: live.consent === "ok_or_leave" ? "By staying you agree to be recorded. You can leave now if you do not agree." : "Until you agree to the recording you can watch and listen, but not use your microphone or camera." }),
12252
+ /* @__PURE__ */ jsxs4("div", { className: "mx-meet__menu-actions", children: [
12253
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__send", onClick: live.agree, children: live.consent === "ok_or_leave" ? "OK" : "I agree" }),
12254
+ live.consent === "ok_or_leave" ? /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: () => void room.leave(), children: "Leave meeting" }) : null
12255
+ ] })
12256
+ ] }) }) : null,
12257
+ snapshot?.canReclaim === true && !isHostNow ? /* @__PURE__ */ jsxs4("div", { className: "mx-meet__notice-bar", role: "status", "data-meet-notice": "host-reclaim", children: [
12258
+ /* @__PURE__ */ jsx6("span", { children: "You started this meeting. Take back the host?" }),
12259
+ /* @__PURE__ */ jsx6(
12260
+ "button",
12261
+ {
12262
+ type: "button",
12263
+ className: "mx-meet__primary mx-meet__primary--small",
12264
+ onClick: () => void controls.run({ action: "reclaim_host" }),
12265
+ children: "Reclaim host"
12266
+ }
12267
+ )
12268
+ ] }) : null,
12269
+ live.micLocked || live.cameraLocked ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "mic-locked", children: live.micLocked && live.cameraLocked ? "Your microphone and camera are turned off by the organizer." : live.micLocked ? "Your microphone is turned off by the organizer." : "Your camera is turned off by the organizer." }) : null,
12270
+ live.timeWarning && live.minutesLeft !== null && live.minutesLeft > 0 ? /* @__PURE__ */ jsxs4("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "time-warning", children: [
12271
+ "This meeting will end in ",
12272
+ live.minutesLeft,
12273
+ " ",
12274
+ live.minutesLeft === 1 ? "minute" : "minutes",
12275
+ " (time limit)."
12276
+ ] }) : null,
11625
12277
  notice !== null && notice.kind !== "removed" ? (
11626
12278
  // A host's request ("the host is asking you to unmute"), not an error:
11627
12279
  // an assertive status, so it is announced at once without being filed
11628
12280
  // as a failure (design-system error-box census).
11629
- /* @__PURE__ */ jsxs3("div", { className: "mx-meet__notice-bar", role: "status", "aria-live": "assertive", children: [
12281
+ /* @__PURE__ */ jsxs4("div", { className: "mx-meet__notice-bar", role: "status", "aria-live": "assertive", children: [
11630
12282
  /* @__PURE__ */ jsx6("span", { children: notice.text }),
11631
12283
  notice.kind === "ask-unmute" && room.local?.micEnabled !== true ? /* @__PURE__ */ jsx6(
11632
12284
  "button",
@@ -11653,7 +12305,7 @@ function ShortcutsSheet({ onClose }) {
11653
12305
  window.addEventListener("keydown", onKey);
11654
12306
  return () => window.removeEventListener("keydown", onKey);
11655
12307
  }, [onClose]);
11656
- return /* @__PURE__ */ jsx6("div", { className: "mx-meet__sheet-backdrop", onClick: onClose, children: /* @__PURE__ */ jsxs3(
12308
+ return /* @__PURE__ */ jsx6("div", { className: "mx-meet__sheet-backdrop", onClick: onClose, children: /* @__PURE__ */ jsxs4(
11657
12309
  "div",
11658
12310
  {
11659
12311
  className: "mx-meet__sheet",
@@ -11661,11 +12313,11 @@ function ShortcutsSheet({ onClose }) {
11661
12313
  "aria-label": "Keyboard shortcuts",
11662
12314
  onClick: (event) => event.stopPropagation(),
11663
12315
  children: [
11664
- /* @__PURE__ */ jsxs3("header", { className: "mx-meet__panel-head", children: [
12316
+ /* @__PURE__ */ jsxs4("header", { className: "mx-meet__panel-head", children: [
11665
12317
  /* @__PURE__ */ jsx6("h2", { className: "mx-meet__panel-title", children: "Keyboard shortcuts" }),
11666
12318
  /* @__PURE__ */ jsx6(ControlButton, { label: "Close", onClick: onClose, children: /* @__PURE__ */ jsx6(CloseIcon, { width: 16, height: 16 }) })
11667
12319
  ] }),
11668
- /* @__PURE__ */ jsx6("dl", { className: "mx-meet__shortcuts", children: MEET_SHORTCUTS.map((shortcut) => /* @__PURE__ */ jsxs3("div", { className: "mx-meet__shortcut", children: [
12320
+ /* @__PURE__ */ jsx6("dl", { className: "mx-meet__shortcuts", children: MEET_SHORTCUTS.map((shortcut) => /* @__PURE__ */ jsxs4("div", { className: "mx-meet__shortcut", children: [
11669
12321
  /* @__PURE__ */ jsx6("dt", { children: shortcut.label }),
11670
12322
  /* @__PURE__ */ jsx6("dd", { children: /* @__PURE__ */ jsx6("kbd", { className: "mx-meet__kbd", children: shortcut.keys }) })
11671
12323
  ] }, shortcut.id)) })
@@ -11697,8 +12349,8 @@ function PanelShell({
11697
12349
  observer.observe(header);
11698
12350
  return () => observer.disconnect();
11699
12351
  }, []);
11700
- return /* @__PURE__ */ jsxs3("aside", { ref, className: "mx-meet__panel", "aria-label": title, children: [
11701
- /* @__PURE__ */ jsxs3("header", { className: "mx-meet__panel-head", children: [
12352
+ return /* @__PURE__ */ jsxs4("aside", { ref, className: "mx-meet__panel", "aria-label": title, children: [
12353
+ /* @__PURE__ */ jsxs4("header", { className: "mx-meet__panel-head", children: [
11702
12354
  /* @__PURE__ */ jsx6("h2", { className: "mx-meet__panel-title", children: title }),
11703
12355
  /* @__PURE__ */ jsx6(ControlButton, { label: "Close panel", onClick: onClose, children: /* @__PURE__ */ jsx6(CloseIcon, { width: 16, height: 16 }) })
11704
12356
  ] }),
@@ -11712,8 +12364,9 @@ function ChatPanel({
11712
12364
  }) {
11713
12365
  const snapshot = useMeetSnapshot();
11714
12366
  const room = useRoom();
11715
- const [draft, setDraft] = useState4("");
11716
- const [recipient, setRecipient] = useState4(to);
12367
+ const live = useLivePolicy();
12368
+ const [draft, setDraft] = useState5("");
12369
+ const [recipient, setRecipient] = useState5(to);
11717
12370
  const listRef = useRef6(null);
11718
12371
  const messages = snapshot?.chat ?? [];
11719
12372
  const local = snapshot?.localIdentity ?? null;
@@ -11730,13 +12383,13 @@ function ChatPanel({
11730
12383
  setRecipient(next);
11731
12384
  onTo?.(next);
11732
12385
  };
11733
- return /* @__PURE__ */ jsxs3(PanelShell, { title: "Chat", onClose, children: [
11734
- /* @__PURE__ */ jsxs3("ul", { className: "mx-meet__chat", ref: listRef, children: [
12386
+ return /* @__PURE__ */ jsxs4(PanelShell, { title: "Chat", onClose, children: [
12387
+ /* @__PURE__ */ jsxs4("ul", { className: "mx-meet__chat", ref: listRef, children: [
11735
12388
  snapshot?.chatHistoryError != null ? /* @__PURE__ */ jsx6("li", { className: "mx-meet__panel-note", children: snapshot.chatHistoryError }) : null,
11736
12389
  snapshot?.chatLoaded !== true && messages.length === 0 ? /* @__PURE__ */ jsx6("li", { className: "mx-meet__panel-note", children: "Reading earlier messages\u2026" }) : messages.length === 0 && snapshot?.chatHistoryError == null ? /* @__PURE__ */ jsx6("li", { className: "mx-meet__panel-note", children: "No messages yet. Messages to everyone stay with the meeting, so people who join later see them." }) : null,
11737
12390
  messages.map((message) => /* @__PURE__ */ jsx6(ChatLine, { message, local, people }, message.id))
11738
12391
  ] }),
11739
- /* @__PURE__ */ jsxs3(
12392
+ !live.canChat ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__panel-note", role: "status", "data-meet-notice": "chat-restricted", children: live.chatMode === "off" ? "Chat is turned off for this meeting." : "Chat is limited to the host and co-hosts in this meeting." }) : /* @__PURE__ */ jsxs4(
11740
12393
  "form",
11741
12394
  {
11742
12395
  className: "mx-meet__composer mx-meet__composer--chat",
@@ -11749,7 +12402,7 @@ function ChatPanel({
11749
12402
  else void room.sendPrivateChat(recipient, text3);
11750
12403
  },
11751
12404
  children: [
11752
- people.length > 0 ? /* @__PURE__ */ jsxs3(
12405
+ people.length > 0 ? /* @__PURE__ */ jsxs4(
11753
12406
  "select",
11754
12407
  {
11755
12408
  className: "mx-meet__select mx-meet__select--compact",
@@ -11758,14 +12411,14 @@ function ChatPanel({
11758
12411
  onChange: (event) => choose(event.target.value === "" ? null : event.target.value),
11759
12412
  children: [
11760
12413
  /* @__PURE__ */ jsx6("option", { value: "", children: "Everyone" }),
11761
- people.map((participant) => /* @__PURE__ */ jsxs3("option", { value: participant.identity, children: [
12414
+ people.map((participant) => /* @__PURE__ */ jsxs4("option", { value: participant.identity, children: [
11762
12415
  participant.displayName,
11763
12416
  " (private)"
11764
12417
  ] }, participant.identity))
11765
12418
  ]
11766
12419
  }
11767
12420
  ) : null,
11768
- /* @__PURE__ */ jsxs3("div", { className: "mx-meet__composer-row", children: [
12421
+ /* @__PURE__ */ jsxs4("div", { className: "mx-meet__composer-row", children: [
11769
12422
  /* @__PURE__ */ jsx6(
11770
12423
  "input",
11771
12424
  {
@@ -11779,7 +12432,7 @@ function ChatPanel({
11779
12432
  ),
11780
12433
  /* @__PURE__ */ jsx6("button", { type: "submit", className: "mx-meet__send", disabled: draft.trim().length === 0, children: "Send" })
11781
12434
  ] }),
11782
- recipientName !== null ? /* @__PURE__ */ jsxs3("p", { className: "mx-meet__panel-note", children: [
12435
+ recipientName !== null ? /* @__PURE__ */ jsxs4("p", { className: "mx-meet__panel-note", children: [
11783
12436
  "Only ",
11784
12437
  recipientName,
11785
12438
  " sees this. Private messages are not saved."
@@ -11797,7 +12450,7 @@ function ChatLine({
11797
12450
  const mine = message.identity === local;
11798
12451
  const privateTo = message.to ?? null;
11799
12452
  const counterpart = privateTo === null ? null : mine ? people.find((p) => p.identity === privateTo)?.displayName ?? "them" : "you";
11800
- return /* @__PURE__ */ jsxs3(
12453
+ return /* @__PURE__ */ jsxs4(
11801
12454
  "li",
11802
12455
  {
11803
12456
  className: [
@@ -11806,9 +12459,9 @@ function ChatLine({
11806
12459
  message.failed !== void 0 ? "mx-meet__chat-line--failed" : ""
11807
12460
  ].filter(Boolean).join(" "),
11808
12461
  children: [
11809
- /* @__PURE__ */ jsxs3("span", { className: "mx-meet__chat-meta", children: [
12462
+ /* @__PURE__ */ jsxs4("span", { className: "mx-meet__chat-meta", children: [
11810
12463
  /* @__PURE__ */ jsx6("strong", { children: mine ? "You" : message.displayName }),
11811
- counterpart !== null ? /* @__PURE__ */ jsxs3("span", { className: "mx-meet__chat-private", children: [
12464
+ counterpart !== null ? /* @__PURE__ */ jsxs4("span", { className: "mx-meet__chat-private", children: [
11812
12465
  "to ",
11813
12466
  counterpart,
11814
12467
  " \xB7 private"
@@ -11828,8 +12481,8 @@ function PeoplePanel({
11828
12481
  const snapshot = useMeetSnapshot();
11829
12482
  const controls = useHostControls();
11830
12483
  const queue = useHandQueue();
11831
- const [query, setQuery] = useState4("");
11832
- const [menuFor, setMenuFor] = useState4(null);
12484
+ const [query, setQuery] = useState5("");
12485
+ const [menuFor, setMenuFor] = useState5(null);
11833
12486
  const myRole = useRole();
11834
12487
  const local = snapshot?.localIdentity ?? null;
11835
12488
  const observersHidden = snapshot?.grants.observersVisibleTo === "hosts" && !controls.isHost;
@@ -11838,11 +12491,11 @@ function PeoplePanel({
11838
12491
  );
11839
12492
  const needle = query.trim().toLowerCase();
11840
12493
  const shown = needle.length === 0 ? everyone : everyone.filter((participant) => participant.displayName.toLowerCase().includes(needle));
11841
- return /* @__PURE__ */ jsxs3(PanelShell, { title: `People (${everyone.length})`, onClose, children: [
12494
+ return /* @__PURE__ */ jsxs4(PanelShell, { title: `People (${everyone.length})`, onClose, children: [
11842
12495
  /* @__PURE__ */ jsx6(LobbyPanel, {}),
11843
- queue.length > 0 ? /* @__PURE__ */ jsxs3("section", { className: "mx-meet__section", "aria-label": "Raised hands", children: [
11844
- /* @__PURE__ */ jsxs3("header", { className: "mx-meet__section-head", children: [
11845
- /* @__PURE__ */ jsxs3("h3", { className: "mx-meet__section-title", children: [
12496
+ queue.length > 0 ? /* @__PURE__ */ jsxs4("section", { className: "mx-meet__section", "aria-label": "Raised hands", children: [
12497
+ /* @__PURE__ */ jsxs4("header", { className: "mx-meet__section-head", children: [
12498
+ /* @__PURE__ */ jsxs4("h3", { className: "mx-meet__section-title", children: [
11846
12499
  "Raised hands \xB7 ",
11847
12500
  queue.length
11848
12501
  ] }),
@@ -11856,9 +12509,9 @@ function PeoplePanel({
11856
12509
  }
11857
12510
  ) : null
11858
12511
  ] }),
11859
- /* @__PURE__ */ jsx6("ol", { className: "mx-meet__queue", children: queue.map((participant) => /* @__PURE__ */ jsxs3("li", { className: "mx-meet__person", children: [
12512
+ /* @__PURE__ */ jsx6("ol", { className: "mx-meet__queue", children: queue.map((participant) => /* @__PURE__ */ jsxs4("li", { className: "mx-meet__person", children: [
11860
12513
  /* @__PURE__ */ jsx6(Avatar, { name: participant.displayName, url: participant.avatarUrl, size: 28 }),
11861
- /* @__PURE__ */ jsxs3("span", { className: "mx-meet__person-name", children: [
12514
+ /* @__PURE__ */ jsxs4("span", { className: "mx-meet__person-name", children: [
11862
12515
  participant.displayName,
11863
12516
  participant.identity === local ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__you", children: " (you)" }) : null
11864
12517
  ] }),
@@ -11873,12 +12526,12 @@ function PeoplePanel({
11873
12526
  ) : null
11874
12527
  ] }, participant.identity)) })
11875
12528
  ] }) : null,
11876
- controls.isHost && everyone.length > 1 ? /* @__PURE__ */ jsxs3("div", { className: "mx-meet__bulk", children: [
11877
- /* @__PURE__ */ jsxs3("button", { type: "button", className: "mx-meet__chip", onClick: () => void controls.run({ action: "mute_all" }), children: [
12529
+ controls.isHost && everyone.length > 1 ? /* @__PURE__ */ jsxs4("div", { className: "mx-meet__bulk", children: [
12530
+ /* @__PURE__ */ jsxs4("button", { type: "button", className: "mx-meet__chip", onClick: () => void controls.run({ action: "mute_all" }), children: [
11878
12531
  /* @__PURE__ */ jsx6(MicOffIcon, { width: 14, height: 14 }),
11879
12532
  " Mute all"
11880
12533
  ] }),
11881
- /* @__PURE__ */ jsxs3(
12534
+ /* @__PURE__ */ jsxs4(
11882
12535
  "button",
11883
12536
  {
11884
12537
  type: "button",
@@ -11891,7 +12544,7 @@ function PeoplePanel({
11891
12544
  }
11892
12545
  )
11893
12546
  ] }) : null,
11894
- everyone.length > 4 ? /* @__PURE__ */ jsxs3("label", { className: "mx-meet__search", children: [
12547
+ everyone.length > 4 ? /* @__PURE__ */ jsxs4("label", { className: "mx-meet__search", children: [
11895
12548
  /* @__PURE__ */ jsx6(SearchIcon, { width: 16, height: 16 }),
11896
12549
  /* @__PURE__ */ jsx6(
11897
12550
  "input",
@@ -11905,10 +12558,10 @@ function PeoplePanel({
11905
12558
  )
11906
12559
  ] }) : null,
11907
12560
  controls.notice !== null ? /* @__PURE__ */ jsx6("p", { className: controls.notice.failed ? "mx-meet__error" : "mx-meet__notice", role: "status", children: controls.notice.text }) : null,
11908
- /* @__PURE__ */ jsxs3("ul", { className: "mx-meet__people", children: [
11909
- shown.map((participant) => /* @__PURE__ */ jsxs3("li", { className: "mx-meet__person", children: [
12561
+ /* @__PURE__ */ jsxs4("ul", { className: "mx-meet__people", children: [
12562
+ shown.map((participant) => /* @__PURE__ */ jsxs4("li", { className: "mx-meet__person", children: [
11910
12563
  /* @__PURE__ */ jsx6(Avatar, { name: participant.displayName, url: participant.avatarUrl, size: 32 }),
11911
- /* @__PURE__ */ jsxs3("span", { className: "mx-meet__person-name", children: [
12564
+ /* @__PURE__ */ jsxs4("span", { className: "mx-meet__person-name", children: [
11912
12565
  participant.displayName,
11913
12566
  participant.identity === local ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__you", children: " (you)" }) : null,
11914
12567
  /* @__PURE__ */ jsx6(
@@ -11925,7 +12578,7 @@ function PeoplePanel({
11925
12578
  participant.handRaised ? /* @__PURE__ */ jsx6(HandIcon, { width: 14, height: 14 }) : null,
11926
12579
  !participant.micEnabled && !isNoteTaker(participant) ? /* @__PURE__ */ jsx6(MicOffIcon, { width: 14, height: 14 }) : null,
11927
12580
  /* @__PURE__ */ jsx6(QualityBars, { quality: participant.quality }),
11928
- /* @__PURE__ */ jsxs3("span", { className: "mx-meet__anchor", children: [
12581
+ /* @__PURE__ */ jsxs4("span", { className: "mx-meet__anchor", children: [
11929
12582
  /* @__PURE__ */ jsx6(
11930
12583
  ControlButton,
11931
12584
  {
@@ -11946,7 +12599,7 @@ function PeoplePanel({
11946
12599
  ) : null
11947
12600
  ] })
11948
12601
  ] }, participant.identity)),
11949
- shown.length === 0 ? /* @__PURE__ */ jsxs3("li", { className: "mx-meet__panel-note", children: [
12602
+ shown.length === 0 ? /* @__PURE__ */ jsxs4("li", { className: "mx-meet__panel-note", children: [
11950
12603
  "Nobody matches \u201C",
11951
12604
  query,
11952
12605
  "\u201D."
@@ -11964,14 +12617,14 @@ function PersonMenu({
11964
12617
  const host = useMeetHost();
11965
12618
  const snapshot = useMeetSnapshot();
11966
12619
  const controls = useHostControls();
11967
- const [mode, setMode] = useState4("menu");
11968
- const [text3, setText] = useState4("");
12620
+ const [mode, setMode] = useState5("menu");
12621
+ const [text3, setText] = useState5("");
11969
12622
  const self = participant.identity === snapshot?.localIdentity;
11970
12623
  const moderate = controls.canModerate(participant);
11971
12624
  const pinned = snapshot?.pinned === participant.identity;
11972
12625
  const spotlit = snapshot?.spotlight === participant.identity;
11973
12626
  const hasAccount = participant.userId !== null;
11974
- const [refused, setRefused] = useState4(false);
12627
+ const [refused, setRefused] = useState5(false);
11975
12628
  const act = (request) => {
11976
12629
  setRefused(false);
11977
12630
  void controls.run(request).then((result) => {
@@ -11982,7 +12635,7 @@ function PersonMenu({
11982
12635
  const refusal2 = refused && controls.notice?.failed === true ? /* @__PURE__ */ jsx6(ErrorBox, { as: "p", className: "mx-meet__error", message: controls.notice.text, operation: "Change this participant", origin: "meet/components", children: controls.notice.text }) : null;
11983
12636
  if (mode === "rename" || mode === "report") {
11984
12637
  const renaming = mode === "rename";
11985
- return /* @__PURE__ */ jsx6(Popover, { label: renaming ? "Rename" : "Report", onClose, placement, children: /* @__PURE__ */ jsxs3(
12638
+ return /* @__PURE__ */ jsx6(Popover, { label: renaming ? "Rename" : "Report", onClose, placement, children: /* @__PURE__ */ jsxs4(
11986
12639
  "form",
11987
12640
  {
11988
12641
  className: "mx-meet__menu-form",
@@ -11994,7 +12647,7 @@ function PersonMenu({
11994
12647
  );
11995
12648
  },
11996
12649
  children: [
11997
- /* @__PURE__ */ jsxs3("label", { className: "mx-meet__field", children: [
12650
+ /* @__PURE__ */ jsxs4("label", { className: "mx-meet__field", children: [
11998
12651
  /* @__PURE__ */ jsx6("span", { className: "mx-meet__field-label", children: renaming ? "Name shown in this meeting" : `What did ${participant.displayName} do?` }),
11999
12652
  renaming ? /* @__PURE__ */ jsx6(
12000
12653
  "input",
@@ -12018,7 +12671,7 @@ function PersonMenu({
12018
12671
  )
12019
12672
  ] }),
12020
12673
  refusal2,
12021
- /* @__PURE__ */ jsxs3("div", { className: "mx-meet__menu-actions", children: [
12674
+ /* @__PURE__ */ jsxs4("div", { className: "mx-meet__menu-actions", children: [
12022
12675
  /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: () => setMode("menu"), children: "Back" }),
12023
12676
  /* @__PURE__ */ jsx6("button", { type: "submit", className: "mx-meet__send", disabled: text3.trim().length === 0 || controls.busy, children: renaming ? "Rename" : "Report" })
12024
12677
  ] })
@@ -12028,10 +12681,10 @@ function PersonMenu({
12028
12681
  }
12029
12682
  if (mode === "remove" || mode === "transfer") {
12030
12683
  const removing = mode === "remove";
12031
- return /* @__PURE__ */ jsx6(Popover, { label: removing ? "Remove" : "Make host", onClose, placement, children: /* @__PURE__ */ jsxs3("div", { className: "mx-meet__menu-form", children: [
12684
+ return /* @__PURE__ */ jsx6(Popover, { label: removing ? "Remove" : "Make host", onClose, placement, children: /* @__PURE__ */ jsxs4("div", { className: "mx-meet__menu-form", children: [
12032
12685
  /* @__PURE__ */ jsx6("p", { className: "mx-meet__confirm-text", children: removing ? `Remove ${participant.displayName}? They leave immediately and cannot rejoin this meeting.` : `Make ${participant.displayName} the host? They get every host control and own this meeting. You stay on as a co-host.` }),
12033
12686
  refusal2,
12034
- /* @__PURE__ */ jsxs3("div", { className: "mx-meet__menu-actions", children: [
12687
+ /* @__PURE__ */ jsxs4("div", { className: "mx-meet__menu-actions", children: [
12035
12688
  /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: () => setMode("menu"), children: "Cancel" }),
12036
12689
  /* @__PURE__ */ jsx6(
12037
12690
  "button",
@@ -12046,8 +12699,8 @@ function PersonMenu({
12046
12699
  ] })
12047
12700
  ] }) });
12048
12701
  }
12049
- return /* @__PURE__ */ jsx6(Popover, { label: `Options for ${participant.displayName}`, onClose, placement, children: /* @__PURE__ */ jsxs3("div", { className: "mx-meet__menu", role: "menu", children: [
12050
- /* @__PURE__ */ jsxs3(
12702
+ return /* @__PURE__ */ jsx6(Popover, { label: `Options for ${participant.displayName}`, onClose, placement, children: /* @__PURE__ */ jsxs4("div", { className: "mx-meet__menu", role: "menu", children: [
12703
+ /* @__PURE__ */ jsxs4(
12051
12704
  MenuItem,
12052
12705
  {
12053
12706
  onSelect: () => {
@@ -12061,7 +12714,7 @@ function PersonMenu({
12061
12714
  ]
12062
12715
  }
12063
12716
  ),
12064
- !self && !participant.isAgent && (onMessage !== void 0 || host !== null) ? /* @__PURE__ */ jsxs3(
12717
+ !self && !participant.isAgent && (onMessage !== void 0 || host !== null) ? /* @__PURE__ */ jsxs4(
12065
12718
  MenuItem,
12066
12719
  {
12067
12720
  onSelect: () => {
@@ -12078,37 +12731,44 @@ function PersonMenu({
12078
12731
  ]
12079
12732
  }
12080
12733
  ) : null,
12081
- controls.isHost ? /* @__PURE__ */ jsxs3(MenuItem, { onSelect: () => act({ action: "spotlight", identity: spotlit ? null : participant.identity }), children: [
12734
+ controls.isHost ? /* @__PURE__ */ jsxs4(MenuItem, { onSelect: () => act({ action: "spotlight", identity: spotlit ? null : participant.identity }), children: [
12082
12735
  /* @__PURE__ */ jsx6(SpotlightIcon, { width: 16, height: 16 }),
12083
12736
  " ",
12084
12737
  spotlit ? "Remove spotlight" : "Spotlight for everyone"
12085
12738
  ] }) : null,
12086
- moderate ? /* @__PURE__ */ jsxs3(Fragment, { children: [
12739
+ moderate ? /* @__PURE__ */ jsxs4(Fragment, { children: [
12087
12740
  /* @__PURE__ */ jsx6("div", { className: "mx-meet__menu-sep", role: "separator" }),
12088
- participant.micEnabled ? /* @__PURE__ */ jsxs3(MenuItem, { onSelect: () => act({ action: "mute", identity: participant.identity }), children: [
12741
+ participant.micEnabled ? /* @__PURE__ */ jsxs4(MenuItem, { onSelect: () => act({ action: "mute", identity: participant.identity }), children: [
12089
12742
  /* @__PURE__ */ jsx6(MicOffIcon, { width: 16, height: 16 }),
12090
12743
  " Mute"
12091
- ] }) : !participant.isAgent ? /* @__PURE__ */ jsxs3(MenuItem, { onSelect: () => act({ action: "ask_unmute", identity: participant.identity }), children: [
12744
+ ] }) : !participant.isAgent ? /* @__PURE__ */ jsxs4(MenuItem, { onSelect: () => act({ action: "ask_unmute", identity: participant.identity }), children: [
12092
12745
  /* @__PURE__ */ jsx6(MicIcon, { width: 16, height: 16 }),
12093
12746
  " Ask to unmute"
12094
12747
  ] }) : null,
12095
- participant.cameraEnabled ? /* @__PURE__ */ jsxs3(MenuItem, { onSelect: () => act({ action: "stop_video", identity: participant.identity }), children: [
12748
+ participant.cameraEnabled ? /* @__PURE__ */ jsxs4(MenuItem, { onSelect: () => act({ action: "stop_video", identity: participant.identity }), children: [
12096
12749
  /* @__PURE__ */ jsx6(CameraOffIcon, { width: 16, height: 16 }),
12097
12750
  " Stop video"
12098
12751
  ] }) : null,
12099
- participant.screenSharing ? /* @__PURE__ */ jsxs3(MenuItem, { onSelect: () => act({ action: "stop_screen", identity: participant.identity }), children: [
12752
+ participant.screenSharing ? /* @__PURE__ */ jsxs4(MenuItem, { onSelect: () => act({ action: "stop_screen", identity: participant.identity }), children: [
12100
12753
  /* @__PURE__ */ jsx6(ScreenShareIcon, { width: 16, height: 16 }),
12101
12754
  " Stop screen share"
12102
12755
  ] }) : null,
12103
- participant.handRaised ? /* @__PURE__ */ jsxs3(MenuItem, { onSelect: () => act({ action: "lower_hand", identity: participant.identity }), children: [
12756
+ participant.handRaised ? /* @__PURE__ */ jsxs4(MenuItem, { onSelect: () => act({ action: "lower_hand", identity: participant.identity }), children: [
12104
12757
  /* @__PURE__ */ jsx6(HandIcon, { width: 16, height: 16 }),
12105
12758
  " Lower hand"
12106
12759
  ] }) : null,
12107
- hasAccount && !participant.isAgent && participant.role !== "cohost" ? /* @__PURE__ */ jsxs3(MenuItem, { onSelect: () => act({ action: "make_cohost", identity: participant.identity }), children: [
12760
+ hasAccount && !participant.isAgent && participant.role !== "cohost" ? /* @__PURE__ */ jsxs4(MenuItem, { onSelect: () => act({ action: "make_cohost", identity: participant.identity }), children: [
12108
12761
  /* @__PURE__ */ jsx6(PeopleIcon, { width: 16, height: 16 }),
12109
12762
  " Make co-host"
12110
12763
  ] }) : null,
12111
- !participant.isAgent ? /* @__PURE__ */ jsxs3(
12764
+ !participant.isAgent && participant.role !== "cohost" && participant.role !== "host" ? participant.role === "presenter" ? /* @__PURE__ */ jsxs4(MenuItem, { onSelect: () => act({ action: "revoke_presenter", identity: participant.identity }), children: [
12765
+ /* @__PURE__ */ jsx6(ScreenShareIcon, { width: 16, height: 16 }),
12766
+ " Remove presenter"
12767
+ ] }) : /* @__PURE__ */ jsxs4(MenuItem, { onSelect: () => act({ action: "make_presenter", identity: participant.identity }), children: [
12768
+ /* @__PURE__ */ jsx6(ScreenShareIcon, { width: 16, height: 16 }),
12769
+ " Make presenter"
12770
+ ] }) : null,
12771
+ !participant.isAgent ? /* @__PURE__ */ jsxs4(
12112
12772
  MenuItem,
12113
12773
  {
12114
12774
  onSelect: () => {
@@ -12122,15 +12782,15 @@ function PersonMenu({
12122
12782
  }
12123
12783
  ) : null
12124
12784
  ] }) : null,
12125
- controls.isOwner && participant.role === "cohost" ? /* @__PURE__ */ jsxs3(MenuItem, { onSelect: () => act({ action: "revoke_cohost", identity: participant.identity }), children: [
12785
+ controls.isOwner && participant.role === "cohost" ? /* @__PURE__ */ jsxs4(MenuItem, { onSelect: () => act({ action: "revoke_cohost", identity: participant.identity }), children: [
12126
12786
  /* @__PURE__ */ jsx6(PeopleIcon, { width: 16, height: 16 }),
12127
12787
  " Remove co-host"
12128
12788
  ] }) : null,
12129
- controls.isOwner && !self && hasAccount && !participant.isAgent ? /* @__PURE__ */ jsxs3(MenuItem, { onSelect: () => setMode("transfer"), children: [
12789
+ controls.isOwner && !self && hasAccount && !participant.isAgent ? /* @__PURE__ */ jsxs4(MenuItem, { onSelect: () => setMode("transfer"), children: [
12130
12790
  /* @__PURE__ */ jsx6(SpotlightIcon, { width: 16, height: 16 }),
12131
12791
  " Make host"
12132
12792
  ] }) : null,
12133
- self ? /* @__PURE__ */ jsxs3(
12793
+ self ? /* @__PURE__ */ jsxs4(
12134
12794
  MenuItem,
12135
12795
  {
12136
12796
  onSelect: () => {
@@ -12143,7 +12803,7 @@ function PersonMenu({
12143
12803
  ]
12144
12804
  }
12145
12805
  ) : null,
12146
- !self && !participant.isAgent ? /* @__PURE__ */ jsxs3(
12806
+ !self && !participant.isAgent ? /* @__PURE__ */ jsxs4(
12147
12807
  MenuItem,
12148
12808
  {
12149
12809
  onSelect: () => {
@@ -12156,7 +12816,7 @@ function PersonMenu({
12156
12816
  ]
12157
12817
  }
12158
12818
  ) : null,
12159
- moderate && !participant.isAgent ? /* @__PURE__ */ jsxs3(MenuItem, { danger: true, onSelect: () => setMode("remove"), children: [
12819
+ moderate && !participant.isAgent ? /* @__PURE__ */ jsxs4(MenuItem, { danger: true, onSelect: () => setMode("remove"), children: [
12160
12820
  /* @__PURE__ */ jsx6(CloseIcon, { width: 16, height: 16 }),
12161
12821
  " Remove from meeting"
12162
12822
  ] }) : null
@@ -12164,7 +12824,7 @@ function PersonMenu({
12164
12824
  }
12165
12825
  function SettingsPanel({ onClose }) {
12166
12826
  const host = useRequiredMeetHost();
12167
- return /* @__PURE__ */ jsxs3(PanelShell, { title: "Settings", onClose, children: [
12827
+ return /* @__PURE__ */ jsxs4(PanelShell, { title: "Settings", onClose, children: [
12168
12828
  /* @__PURE__ */ jsx6(
12169
12829
  DeviceSettings,
12170
12830
  {
@@ -12187,15 +12847,15 @@ function DeviceSettings({
12187
12847
  const speaker = devices?.selected.audiooutput ?? host.devices.resolve("audiooutput");
12188
12848
  const stream = useMicrophoneStream(microphone);
12189
12849
  const level = useAudioLevel(stream);
12190
- const [testing, setTesting] = useState4(false);
12850
+ const [testing, setTesting] = useState5(false);
12191
12851
  useEffect6(() => {
12192
12852
  void host.devices.refresh();
12193
12853
  }, [host]);
12194
- return /* @__PURE__ */ jsxs3("section", { className: "mx-meet__section", "aria-label": "Devices", children: [
12854
+ return /* @__PURE__ */ jsxs4("section", { className: "mx-meet__section", "aria-label": "Devices", children: [
12195
12855
  /* @__PURE__ */ jsx6(DevicePicker, { kind: "audioinput", label: "Microphone", onPick }),
12196
12856
  /* @__PURE__ */ jsx6("div", { className: "mx-meet__meter", role: "meter", "aria-label": "Microphone level", "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": Math.round(level * 100), children: /* @__PURE__ */ jsx6("span", { style: { width: `${Math.min(100, Math.round(level * 160))}%` } }) }),
12197
12857
  /* @__PURE__ */ jsx6(DevicePicker, { kind: "audiooutput", label: "Speaker", onPick }),
12198
- /* @__PURE__ */ jsxs3(
12858
+ /* @__PURE__ */ jsxs4(
12199
12859
  "button",
12200
12860
  {
12201
12861
  type: "button",
@@ -12217,7 +12877,7 @@ function DeviceSettings({
12217
12877
  }
12218
12878
  function EffectsSettings({ preview }) {
12219
12879
  const effects = useMediaEffects();
12220
- const [error, setError] = useState4(null);
12880
+ const [error, setError] = useState5(null);
12221
12881
  const fileRef = useRef6(null);
12222
12882
  const supported = effects.backgroundsSupported || preview != null || videoEffectsSupported();
12223
12883
  const apply = (next) => {
@@ -12230,8 +12890,8 @@ function EffectsSettings({ preview }) {
12230
12890
  );
12231
12891
  }
12232
12892
  };
12233
- return /* @__PURE__ */ jsxs3("section", { className: "mx-meet__section", "aria-label": "Effects", children: [
12234
- /* @__PURE__ */ jsxs3("div", { className: "mx-meet__check", children: [
12893
+ return /* @__PURE__ */ jsxs4("section", { className: "mx-meet__section", "aria-label": "Effects", children: [
12894
+ /* @__PURE__ */ jsxs4("div", { className: "mx-meet__check", children: [
12235
12895
  /* @__PURE__ */ jsx6(
12236
12896
  Checkbox,
12237
12897
  {
@@ -12240,12 +12900,12 @@ function EffectsSettings({ preview }) {
12240
12900
  onCheckedChange: (checked) => apply({ noiseSuppression: checked === true })
12241
12901
  }
12242
12902
  ),
12243
- /* @__PURE__ */ jsxs3("div", { className: "mx-meet__check-text", children: [
12903
+ /* @__PURE__ */ jsxs4("div", { className: "mx-meet__check-text", children: [
12244
12904
  /* @__PURE__ */ jsx6(Label, { htmlFor: "mx-meet-noise", children: "Noise suppression" }),
12245
12905
  /* @__PURE__ */ jsx6("span", { className: "mx-meet__hint", children: "Filters keyboards, fans and background chatter." })
12246
12906
  ] })
12247
12907
  ] }),
12248
- supported ? /* @__PURE__ */ jsxs3("div", { className: "mx-meet__field", children: [
12908
+ supported ? /* @__PURE__ */ jsxs4("div", { className: "mx-meet__field", children: [
12249
12909
  /* @__PURE__ */ jsx6(Label, { children: "Background" }),
12250
12910
  /* @__PURE__ */ jsx6(
12251
12911
  SegmentedControl,
@@ -12339,14 +12999,20 @@ async function playTestSound(sinkId) {
12339
12999
  audio.pause();
12340
13000
  await context.close();
12341
13001
  }
12342
- function useMicrophoneStream(deviceId2) {
12343
- const [stream, setStream] = useState4(null);
13002
+ function useMicrophoneStream(deviceId2, report, attempt = 0) {
13003
+ const [stream, setStream] = useState5(null);
13004
+ const reportRef = useRef6(report);
13005
+ reportRef.current = report;
12344
13006
  useEffect6(() => {
12345
- if (deviceId2 === "off") return void 0;
13007
+ if (deviceId2 === "off") {
13008
+ reportRef.current?.("off");
13009
+ return void 0;
13010
+ }
12346
13011
  let live = true;
12347
13012
  let opened = null;
12348
13013
  const media = globalThis.navigator?.mediaDevices;
12349
13014
  if (media === void 0) return void 0;
13015
+ reportRef.current?.("prompting");
12350
13016
  void media.getUserMedia({ audio: deviceId2 === null ? true : { deviceId: { exact: deviceId2 } } }).then((next) => {
12351
13017
  if (!live) {
12352
13018
  next.getTracks().forEach((track) => track.stop());
@@ -12354,20 +13020,54 @@ function useMicrophoneStream(deviceId2) {
12354
13020
  }
12355
13021
  opened = next;
12356
13022
  setStream(next);
12357
- }).catch(() => setStream(null));
13023
+ reportRef.current?.("on");
13024
+ }).catch((thrown) => {
13025
+ setStream(null);
13026
+ if (live) reportRef.current?.({ error: thrown });
13027
+ });
12358
13028
  return () => {
12359
13029
  live = false;
12360
13030
  opened?.getTracks().forEach((track) => track.stop());
12361
13031
  setStream(null);
12362
13032
  };
12363
- }, [deviceId2]);
13033
+ }, [deviceId2, attempt]);
12364
13034
  return stream;
12365
13035
  }
13036
+ function PreviewStatus({ onRetry }) {
13037
+ const { region, where } = useMediaSupervisor();
13038
+ const [waited, setWaited] = useState5(false);
13039
+ const prompting = region.preview.microphone.state === "prompting" || region.preview.camera.state === "prompting";
13040
+ useEffect6(() => {
13041
+ if (!prompting) {
13042
+ setWaited(false);
13043
+ return void 0;
13044
+ }
13045
+ const timer = setTimeout(() => setWaited(true), 800);
13046
+ return () => clearTimeout(timer);
13047
+ }, [prompting]);
13048
+ const refused = ["microphone", "camera"].flatMap((kind) => {
13049
+ const preview = region.preview[kind];
13050
+ const reason = region.permission[kind] === "denied" ? "denied" : preview.state === "unavailable" ? preview.unavailable?.reason ?? "failed" : null;
13051
+ return reason === null ? [] : [{ kind, reason }];
13052
+ });
13053
+ if (refused.length === 0) {
13054
+ if (!prompting || !waited) return null;
13055
+ return /* @__PURE__ */ jsx6("p", { className: "mx-meet__panel-note", role: "status", "data-meet-notice": "permission-prompt", children: "Allow camera and microphone when your browser asks" });
13056
+ }
13057
+ const first = refused[0];
13058
+ const both = refused.length === 2 && refused[0].reason === refused[1].reason;
13059
+ const label = both ? deviceChipLabel("camera", first.reason).replace(/^Camera/, "Camera and mic") : refused.map((r) => deviceChipLabel(r.kind, r.reason)).join(" \xB7 ");
13060
+ return /* @__PURE__ */ jsxs4("div", { className: "mx-meet__warning", role: "status", "data-meet-notice": deviceNoticeCode(first.kind, first.reason), children: [
13061
+ /* @__PURE__ */ jsx6("strong", { children: label }),
13062
+ /* @__PURE__ */ jsx6("ol", { className: "mx-meet__fix-steps", children: deviceFixSteps(first.kind, first.reason, where).map((step) => /* @__PURE__ */ jsx6("li", { children: step }, step)) }),
13063
+ /* @__PURE__ */ jsx6(Button, { type: "button", variant: "outline", size: "sm", onClick: onRetry, children: "Try again" })
13064
+ ] });
13065
+ }
12366
13066
  function KnockNotice({ onView }) {
12367
13067
  const { knocker, others, admit, dismiss } = useKnockNotice();
12368
13068
  if (knocker === null) return null;
12369
- return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__notice-bar", role: "status", "data-meet-notice": "knock-received", "data-meet-participant-name": knocker.displayName, children: [
12370
- /* @__PURE__ */ jsxs3("span", { children: [
13069
+ return /* @__PURE__ */ jsxs4("div", { className: "mx-meet__notice-bar", role: "status", "data-meet-notice": "knock-received", "data-meet-participant-name": knocker.displayName, children: [
13070
+ /* @__PURE__ */ jsxs4("span", { children: [
12371
13071
  knocker.displayName,
12372
13072
  " wants to join",
12373
13073
  others > 0 ? ` (+${others} more)` : ""
@@ -12391,15 +13091,15 @@ function KnockNotice({ onView }) {
12391
13091
  function LobbyPanel() {
12392
13092
  const lobby = useLobby();
12393
13093
  if (!lobby.canAdmit || lobby.waiting.length === 0 && lobby.declined.length === 0) return null;
12394
- return /* @__PURE__ */ jsxs3(Fragment, { children: [
13094
+ return /* @__PURE__ */ jsxs4(Fragment, { children: [
12395
13095
  lobby.waiting.length > 0 ? /* @__PURE__ */ jsx6(WaitingList, { lobby }) : null,
12396
13096
  lobby.declined.length > 0 ? /* @__PURE__ */ jsx6(DeclinedList, { lobby }) : null
12397
13097
  ] });
12398
13098
  }
12399
13099
  function DeclinedList({ lobby }) {
12400
- return /* @__PURE__ */ jsxs3("section", { className: "mx-meet__lobby", "aria-label": "Declined requests", "data-meet-declined": "", children: [
13100
+ return /* @__PURE__ */ jsxs4("section", { className: "mx-meet__lobby", "aria-label": "Declined requests", "data-meet-declined": "", children: [
12401
13101
  /* @__PURE__ */ jsx6("header", { className: "mx-meet__lobby-head", children: /* @__PURE__ */ jsx6("h3", { className: "mx-meet__lobby-title", children: "Declined" }) }),
12402
- /* @__PURE__ */ jsx6("ul", { className: "mx-meet__lobby-list", children: lobby.declined.map((entry) => /* @__PURE__ */ jsxs3(
13102
+ /* @__PURE__ */ jsx6("ul", { className: "mx-meet__lobby-list", children: lobby.declined.map((entry) => /* @__PURE__ */ jsxs4(
12403
13103
  "li",
12404
13104
  {
12405
13105
  className: "mx-meet__lobby-row",
@@ -12425,12 +13125,12 @@ function DeclinedList({ lobby }) {
12425
13125
  ] });
12426
13126
  }
12427
13127
  function WaitingList({ lobby }) {
12428
- return /* @__PURE__ */ jsxs3("section", { className: "mx-meet__lobby", "aria-label": "Waiting room", children: [
12429
- /* @__PURE__ */ jsxs3("header", { className: "mx-meet__lobby-head", children: [
13128
+ return /* @__PURE__ */ jsxs4("section", { className: "mx-meet__lobby", "aria-label": "Waiting room", children: [
13129
+ /* @__PURE__ */ jsxs4("header", { className: "mx-meet__lobby-head", children: [
12430
13130
  /* @__PURE__ */ jsx6("h3", { className: "mx-meet__lobby-title", children: lobby.waiting.length === 1 ? "1 person is waiting" : `${lobby.waiting.length} people are waiting` }),
12431
13131
  /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: () => void lobby.admitAll(), children: "Admit all" })
12432
13132
  ] }),
12433
- /* @__PURE__ */ jsx6("ul", { className: "mx-meet__lobby-list", children: lobby.waiting.map((entry) => /* @__PURE__ */ jsxs3(
13133
+ /* @__PURE__ */ jsx6("ul", { className: "mx-meet__lobby-list", children: lobby.waiting.map((entry) => /* @__PURE__ */ jsxs4(
12434
13134
  "li",
12435
13135
  {
12436
13136
  className: "mx-meet__lobby-row",
@@ -12438,7 +13138,7 @@ function WaitingList({ lobby }) {
12438
13138
  "data-meet-participant-name": entry.displayName,
12439
13139
  children: [
12440
13140
  /* @__PURE__ */ jsx6(Avatar, { name: entry.displayName, url: entry.avatarUrl, size: 32 }),
12441
- /* @__PURE__ */ jsxs3("span", { className: "mx-meet__person-name", children: [
13141
+ /* @__PURE__ */ jsxs4("span", { className: "mx-meet__person-name", children: [
12442
13142
  entry.displayName,
12443
13143
  entry.userId === null ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__badge", children: "Guest" }) : null
12444
13144
  ] }),
@@ -12462,17 +13162,17 @@ function NoteTakerRow() {
12462
13162
  const ai = useMeetAi();
12463
13163
  const status = ai.noteTaker;
12464
13164
  const duplicateError = status.refusal !== null && ai.error !== null && status.refusal.reason === ai.error.message && status.refusal.remedy === ai.error.remedy;
12465
- return /* @__PURE__ */ jsxs3("section", { className: "mx-meet__note-taker", "aria-label": "Note-taker", children: [
12466
- /* @__PURE__ */ jsxs3("p", { className: `mx-meet__note-taker-state mx-meet__note-taker-state--${status.state}`, children: [
13165
+ return /* @__PURE__ */ jsxs4("section", { className: "mx-meet__note-taker", "aria-label": "Note-taker", children: [
13166
+ /* @__PURE__ */ jsxs4("p", { className: `mx-meet__note-taker-state mx-meet__note-taker-state--${status.state}`, children: [
12467
13167
  /* @__PURE__ */ jsx6("span", { className: "mx-meet__note-taker-dot", "aria-hidden": "true" }),
12468
13168
  ai.noteTakerLabel
12469
13169
  ] }),
12470
13170
  status.detail !== null ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__panel-note", children: status.detail }) : null,
12471
- status.refusal !== null ? /* @__PURE__ */ jsxs3(ErrorBox, { as: "div", className: "mx-meet__refusal", error: status.refusal, operation: "Join this meeting", origin: "meet/components", children: [
13171
+ status.refusal !== null ? /* @__PURE__ */ jsxs4(ErrorBox, { as: "div", className: "mx-meet__refusal", error: status.refusal, operation: "Join this meeting", origin: "meet/components", children: [
12472
13172
  /* @__PURE__ */ jsx6("p", { className: "mx-meet__refusal-reason", children: status.refusal.reason }),
12473
13173
  status.refusal.remedy.length > 0 ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__refusal-remedy", children: status.refusal.remedy }) : null
12474
13174
  ] }) : null,
12475
- ai.error !== null && !duplicateError ? /* @__PURE__ */ jsxs3(ErrorBox, { as: "div", className: "mx-meet__refusal", error: ai.error, operation: "Run the meeting assistant", origin: "meet/components", children: [
13175
+ ai.error !== null && !duplicateError ? /* @__PURE__ */ jsxs4(ErrorBox, { as: "div", className: "mx-meet__refusal", error: ai.error, operation: "Run the meeting assistant", origin: "meet/components", children: [
12476
13176
  /* @__PURE__ */ jsx6("p", { className: "mx-meet__refusal-reason", children: ai.error.message }),
12477
13177
  /* @__PURE__ */ jsx6("p", { className: "mx-meet__refusal-remedy", children: ai.error.remedy })
12478
13178
  ] }) : null,
@@ -12500,9 +13200,9 @@ function NoteList({
12500
13200
  notes,
12501
13201
  empty
12502
13202
  }) {
12503
- return /* @__PURE__ */ jsxs3("section", { className: "mx-meet__note-group", "aria-label": title, children: [
13203
+ return /* @__PURE__ */ jsxs4("section", { className: "mx-meet__note-group", "aria-label": title, children: [
12504
13204
  /* @__PURE__ */ jsx6("h3", { className: "mx-meet__note-group-title", children: title }),
12505
- notes.length === 0 ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__panel-note", children: empty }) : /* @__PURE__ */ jsx6("ul", { className: "mx-meet__notes", children: notes.map((note) => /* @__PURE__ */ jsxs3("li", { className: "mx-meet__note", children: [
13205
+ notes.length === 0 ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__panel-note", children: empty }) : /* @__PURE__ */ jsx6("ul", { className: "mx-meet__notes", children: notes.map((note) => /* @__PURE__ */ jsxs4("li", { className: "mx-meet__note", children: [
12506
13206
  note.text,
12507
13207
  note.assigneeDisplayName !== void 0 && note.assigneeDisplayName !== null ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__assignee", children: note.assigneeDisplayName }) : null
12508
13208
  ] }, note.id)) })
@@ -12510,14 +13210,14 @@ function NoteList({
12510
13210
  }
12511
13211
  function AiPanel({ onClose }) {
12512
13212
  const ai = useMeetAi();
12513
- const [question, setQuestion] = useState4("");
12514
- return /* @__PURE__ */ jsxs3(PanelShell, { title: "Meeting assistant", onClose, children: [
13213
+ const [question, setQuestion] = useState5("");
13214
+ return /* @__PURE__ */ jsxs4(PanelShell, { title: "Meeting assistant", onClose, children: [
12515
13215
  /* @__PURE__ */ jsx6(NoteTakerRow, {}),
12516
- ai.summary !== null ? /* @__PURE__ */ jsxs3("section", { className: "mx-meet__note-group", "aria-label": "Summary", children: [
13216
+ ai.summary !== null ? /* @__PURE__ */ jsxs4("section", { className: "mx-meet__note-group", "aria-label": "Summary", children: [
12517
13217
  /* @__PURE__ */ jsx6("h3", { className: "mx-meet__note-group-title", children: "Summary" }),
12518
13218
  /* @__PURE__ */ jsx6("p", { className: "mx-meet__summary", children: ai.summary.text })
12519
13219
  ] }) : null,
12520
- ai.wrapUp !== null ? /* @__PURE__ */ jsxs3("p", { className: "mx-meet__wrap-up-notice", role: "status", children: [
13220
+ ai.wrapUp !== null ? /* @__PURE__ */ jsxs4("p", { className: "mx-meet__wrap-up-notice", role: "status", children: [
12521
13221
  "The wrap-up is written \u2014 ",
12522
13222
  ai.wrapUp.noteIds.length,
12523
13223
  " ",
@@ -12553,9 +13253,9 @@ function AiPanel({ onClose }) {
12553
13253
  empty: "No action items yet."
12554
13254
  }
12555
13255
  ),
12556
- ai.transcript.length > 0 ? /* @__PURE__ */ jsxs3("section", { className: "mx-meet__note-group", "aria-label": "Transcript", children: [
13256
+ ai.transcript.length > 0 ? /* @__PURE__ */ jsxs4("section", { className: "mx-meet__note-group", "aria-label": "Transcript", children: [
12557
13257
  /* @__PURE__ */ jsx6("h3", { className: "mx-meet__note-group-title", children: "Transcript" }),
12558
- /* @__PURE__ */ jsx6("ul", { className: "mx-meet__transcript", children: ai.transcript.map((line) => /* @__PURE__ */ jsxs3(
13258
+ /* @__PURE__ */ jsx6("ul", { className: "mx-meet__transcript", children: ai.transcript.map((line) => /* @__PURE__ */ jsxs4(
12559
13259
  "li",
12560
13260
  {
12561
13261
  className: line.final && line.source === "record" ? "mx-meet__line" : "mx-meet__line mx-meet__line--interim",
@@ -12567,7 +13267,7 @@ function AiPanel({ onClose }) {
12567
13267
  line.id
12568
13268
  )) })
12569
13269
  ] }) : null,
12570
- /* @__PURE__ */ jsxs3(
13270
+ /* @__PURE__ */ jsxs4(
12571
13271
  "form",
12572
13272
  {
12573
13273
  className: "mx-meet__composer",
@@ -12594,7 +13294,7 @@ function AiPanel({ onClose }) {
12594
13294
  ),
12595
13295
  ai.asking && ai.phase !== null ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__panel-note", role: "status", children: ai.phase }) : null,
12596
13296
  ai.answer !== null ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__answer", children: ai.answer }) : null,
12597
- ai.answer !== null && ai.coverage !== null ? /* @__PURE__ */ jsxs3("p", { className: "mx-meet__coverage", children: [
13297
+ ai.answer !== null && ai.coverage !== null ? /* @__PURE__ */ jsxs4("p", { className: "mx-meet__coverage", children: [
12598
13298
  "Answered from ",
12599
13299
  ai.coverage.segmentsUsed,
12600
13300
  " of ",
@@ -12603,7 +13303,7 @@ function AiPanel({ onClose }) {
12603
13303
  ai.coverage.truncated ? " (the oldest were left out)" : "",
12604
13304
  "."
12605
13305
  ] }) : null,
12606
- ai.error !== null ? /* @__PURE__ */ jsxs3(ErrorBox, { as: "div", className: "mx-meet__refusal", error: ai.error, operation: "Run the meeting assistant", origin: "meet/components", children: [
13306
+ ai.error !== null ? /* @__PURE__ */ jsxs4(ErrorBox, { as: "div", className: "mx-meet__refusal", error: ai.error, operation: "Run the meeting assistant", origin: "meet/components", children: [
12607
13307
  /* @__PURE__ */ jsx6("p", { className: "mx-meet__refusal-reason", children: ai.error.message }),
12608
13308
  /* @__PURE__ */ jsx6("p", { className: "mx-meet__refusal-remedy", children: ai.error.remedy })
12609
13309
  ] }) : null
@@ -12613,11 +13313,22 @@ function Captions({ lines = 3 }) {
12613
13313
  const ai = useMeetAi();
12614
13314
  const visible = ai.transcript.slice(-lines);
12615
13315
  const translation = useCaptionTranslation(visible);
12616
- if (visible.length === 0) return null;
12617
- return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__captions", "aria-live": "polite", "aria-label": "Live captions", children: [
13316
+ const captions = useCaptions();
13317
+ if (!captions.on) return null;
13318
+ if (captions.state === "unavailable") {
13319
+ return /* @__PURE__ */ jsx6("div", { className: "mx-meet__captions", role: "status", "data-meet-notice": "captions-unavailable", children: /* @__PURE__ */ jsxs4("p", { className: "mx-meet__caption", children: [
13320
+ "Captions are unavailable. ",
13321
+ captions.reason,
13322
+ captions.remedy !== null ? ` ${captions.remedy}` : ""
13323
+ ] }) });
13324
+ }
13325
+ if (visible.length === 0) {
13326
+ 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." }) });
13327
+ }
13328
+ return /* @__PURE__ */ jsxs4("div", { className: "mx-meet__captions", "aria-live": "polite", "aria-label": "Live captions", "data-meet-caption": "live", children: [
12618
13329
  visible.map((line) => {
12619
13330
  const translated = translation.language === null ? void 0 : translation.translations[line.id];
12620
- return /* @__PURE__ */ jsxs3(
13331
+ return /* @__PURE__ */ jsxs4(
12621
13332
  "p",
12622
13333
  {
12623
13334
  className: line.final ? "mx-meet__caption" : "mx-meet__caption mx-meet__caption--interim",
@@ -12637,7 +13348,7 @@ function Captions({ lines = 3 }) {
12637
13348
  function ConsentNotice() {
12638
13349
  const notice = useConsentNotice();
12639
13350
  if (notice === null) return null;
12640
- return /* @__PURE__ */ jsx6("p", { className: "mx-meet__consent", role: "status", children: notice });
13351
+ return /* @__PURE__ */ jsx6("p", { className: "mx-meet__consent", role: "status", "data-meet-notice": "notes-started", children: notice });
12641
13352
  }
12642
13353
  function AttendanceNotice() {
12643
13354
  const media = useLocalMedia();
@@ -12645,7 +13356,7 @@ function AttendanceNotice() {
12645
13356
  if (notice === null) return null;
12646
13357
  const refused = media.microphone.unavailable;
12647
13358
  const code = microphoneNoticeCode(media.microphone);
12648
- return /* @__PURE__ */ jsxs3(
13359
+ return /* @__PURE__ */ jsxs4(
12649
13360
  "p",
12650
13361
  {
12651
13362
  className: "mx-meet__banner mx-meet__banner--attendance",
@@ -12676,15 +13387,22 @@ function PreJoinBody({
12676
13387
  const recording = useRecording();
12677
13388
  const effects = useMediaEffects();
12678
13389
  const { notice } = useModerationNotice();
12679
- const [mode, setMode] = useState4("video");
12680
- const [microphone, setMicrophone] = useState4(true);
12681
- const [joining, setJoining] = useState4(false);
13390
+ const [mode, setMode] = useState5("video");
13391
+ const [microphone, setMicrophone] = useState5(true);
13392
+ const [joining, setJoining] = useState5(false);
12682
13393
  const camera = devices?.selected.videoinput ?? host.devices.resolve("videoinput");
12683
13394
  const mic = devices?.selected.audioinput ?? host.devices.resolve("audioinput");
12684
- const preview = useCameraPreview(mode === "video" ? camera : void 0, effects.effects);
12685
- const micStream = useMicrophoneStream(microphone ? mic : "off");
13395
+ const { reportPreview } = useMediaSupervisor();
13396
+ const [attempt, setAttempt] = useState5(0);
13397
+ const preview = useCameraPreview(
13398
+ mode === "video" ? camera : void 0,
13399
+ effects.effects,
13400
+ (outcome) => reportPreview("camera", outcome),
13401
+ attempt
13402
+ );
13403
+ const micStream = useMicrophoneStream(microphone ? mic : "off", (outcome) => reportPreview("microphone", outcome), attempt);
12686
13404
  const level = useAudioLevel(micStream);
12687
- const [speakerTesting, setSpeakerTesting] = useState4(false);
13405
+ const [speakerTesting, setSpeakerTesting] = useState5(false);
12688
13406
  useEffect6(() => {
12689
13407
  void host.devices.refresh();
12690
13408
  }, [host]);
@@ -12707,8 +13425,8 @@ function PreJoinBody({
12707
13425
  ...secondDevice !== void 0 ? { secondDevice } : {}
12708
13426
  }).then(() => onJoined?.()).catch(() => void 0).finally(() => setJoining(false));
12709
13427
  };
12710
- return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__prejoin", children: [
12711
- /* @__PURE__ */ jsxs3("div", { className: "mx-meet__preview", children: [
13428
+ return /* @__PURE__ */ jsxs4("div", { className: "mx-meet__prejoin", children: [
13429
+ /* @__PURE__ */ jsxs4("div", { className: "mx-meet__preview", children: [
12712
13430
  mode === "video" && preview.preview !== null ? /* @__PURE__ */ jsx6(
12713
13431
  "video",
12714
13432
  {
@@ -12724,10 +13442,11 @@ function PreJoinBody({
12724
13442
  mode === "video" && preview.error !== null ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__preview-note", children: preview.error }) : null,
12725
13443
  microphone && micStream !== null ? /* @__PURE__ */ jsx6("div", { className: "mx-meet__meter mx-meet__meter--preview", role: "meter", "aria-label": "Microphone level", "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": Math.round(level * 100), children: /* @__PURE__ */ jsx6("span", { style: { width: `${Math.min(100, Math.round(level * 160))}%` } }) }) : null
12726
13444
  ] }),
12727
- /* @__PURE__ */ jsxs3("div", { className: "mx-meet__prejoin-panel", children: [
13445
+ /* @__PURE__ */ jsxs4("div", { className: "mx-meet__prejoin-panel", children: [
12728
13446
  /* @__PURE__ */ jsx6("h1", { className: "mx-meet__prejoin-title", children: "Ready to join?" }),
13447
+ /* @__PURE__ */ jsx6(PreviewStatus, { onRetry: () => setAttempt((n) => n + 1) }),
12729
13448
  notice?.kind === "removed" ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__warning", children: notice.text }) : null,
12730
- devices?.labelsHidden === true ? /* @__PURE__ */ jsxs3("p", { className: "mx-meet__panel-note", children: [
13449
+ devices?.labelsHidden === true ? /* @__PURE__ */ jsxs4("p", { className: "mx-meet__panel-note", children: [
12731
13450
  "Your browser is hiding device names until you allow camera and microphone access.",
12732
13451
  /* @__PURE__ */ jsx6(
12733
13452
  "button",
@@ -12743,9 +13462,9 @@ function PreJoinBody({
12743
13462
  ] }) : null,
12744
13463
  devices?.fallbackNotice != null ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__warning", children: devices.fallbackNotice }) : null,
12745
13464
  /* @__PURE__ */ jsx6(DevicePicker, { kind: "audioinput", label: "Microphone" }),
12746
- /* @__PURE__ */ jsxs3("div", { className: "mx-meet__field-row", children: [
13465
+ /* @__PURE__ */ jsxs4("div", { className: "mx-meet__field-row", children: [
12747
13466
  /* @__PURE__ */ jsx6(DevicePicker, { kind: "audiooutput", label: "Speaker" }),
12748
- /* @__PURE__ */ jsxs3(
13467
+ /* @__PURE__ */ jsxs4(
12749
13468
  Button,
12750
13469
  {
12751
13470
  type: "button",
@@ -12768,8 +13487,8 @@ function PreJoinBody({
12768
13487
  /* @__PURE__ */ jsx6(DevicePicker, { kind: "videoinput", label: "Camera" }),
12769
13488
  /* @__PURE__ */ jsx6(EffectsSettings, { preview: preview.preview }),
12770
13489
  recording.consentNotice !== null ? /* @__PURE__ */ jsx6(Alert, { variant: "info", children: /* @__PURE__ */ jsx6(AlertDescription, { children: recording.consentNotice }) }) : null,
12771
- /* @__PURE__ */ jsxs3("div", { className: "mx-meet__prejoin-actions", children: [
12772
- /* @__PURE__ */ jsxs3("div", { className: "mx-meet__check", children: [
13490
+ /* @__PURE__ */ jsxs4("div", { className: "mx-meet__prejoin-actions", children: [
13491
+ /* @__PURE__ */ jsxs4("div", { className: "mx-meet__check", children: [
12773
13492
  /* @__PURE__ */ jsx6(
12774
13493
  Checkbox,
12775
13494
  {
@@ -12780,7 +13499,7 @@ function PreJoinBody({
12780
13499
  ),
12781
13500
  /* @__PURE__ */ jsx6(Label, { htmlFor: "mx-meet-join-video", children: "Join with video" })
12782
13501
  ] }),
12783
- /* @__PURE__ */ jsxs3("div", { className: "mx-meet__check", children: [
13502
+ /* @__PURE__ */ jsxs4("div", { className: "mx-meet__check", children: [
12784
13503
  /* @__PURE__ */ jsx6(
12785
13504
  Checkbox,
12786
13505
  {
@@ -12794,7 +13513,7 @@ function PreJoinBody({
12794
13513
  room.secondDevice === null ? /* @__PURE__ */ jsx6(Button, { type: "button", className: "mx-meet__join", disabled: joining, onClick: () => joinWith(), children: joining ? "Joining\u2026" : observe2 ? "Observe" : "Join now" }) : (
12795
13514
  // S7: ALREADY IN THIS MEETING on another tab or device — the meeting's choice
12796
13515
  // (Meet: Switch here / Join here too; Teams: Transfer here / Add this device).
12797
- /* @__PURE__ */ jsxs3("div", { className: "mx-meet__second-device", "data-meet-region": "second-device", children: [
13516
+ /* @__PURE__ */ jsxs4("div", { className: "mx-meet__second-device", "data-meet-region": "second-device", children: [
12798
13517
  /* @__PURE__ */ jsx6("p", { className: "mx-meet__hint", children: "You're in this meeting on another device" }),
12799
13518
  /* @__PURE__ */ jsx6(
12800
13519
  Button,
@@ -12821,23 +13540,29 @@ function PreJoinBody({
12821
13540
  ] })
12822
13541
  )
12823
13542
  ] }),
12824
- room.failure !== null ? /* @__PURE__ */ jsxs3("p", { className: "mx-meet__error", children: [
13543
+ room.failure !== null ? /* @__PURE__ */ jsxs4("p", { className: "mx-meet__error", children: [
12825
13544
  room.failure.message,
12826
13545
  /* @__PURE__ */ jsx6("span", { className: "mx-meet__remedy", children: room.failure.remedy })
12827
13546
  ] }) : null
12828
13547
  ] })
12829
13548
  ] });
12830
13549
  }
12831
- function useCameraPreview(deviceId2, effects) {
12832
- const [preview, setPreview] = useState4(null);
12833
- const [error, setError] = useState4(null);
13550
+ function useCameraPreview(deviceId2, effects, report, attempt = 0) {
13551
+ const reportRef = useRef6(report);
13552
+ reportRef.current = report;
13553
+ const [preview, setPreview] = useState5(null);
13554
+ const [error, setError] = useState5(null);
12834
13555
  const current = useRef6(null);
12835
13556
  const initialEffect = useRef6(toVideoEffect(effects));
12836
13557
  initialEffect.current = toVideoEffect(effects);
12837
13558
  useEffect6(() => {
12838
- if (deviceId2 === void 0) return void 0;
13559
+ if (deviceId2 === void 0) {
13560
+ reportRef.current?.("off");
13561
+ return void 0;
13562
+ }
12839
13563
  let live = true;
12840
13564
  setError(null);
13565
+ reportRef.current?.("prompting");
12841
13566
  void createCameraPreview(deviceId2, initialEffect.current).then((created) => {
12842
13567
  if (!live) {
12843
13568
  created.stop();
@@ -12845,8 +13570,10 @@ function useCameraPreview(deviceId2, effects) {
12845
13570
  }
12846
13571
  current.current = created;
12847
13572
  setPreview(created);
13573
+ reportRef.current?.("on");
12848
13574
  }).catch((thrown) => {
12849
13575
  if (!live) return;
13576
+ reportRef.current?.({ error: thrown });
12850
13577
  const failure = thrown;
12851
13578
  const message = (failure.message ?? "").replace(/^meet\.[A-Za-z.()]+:\s*/, "");
12852
13579
  const sentence = message.length > 0 ? message.charAt(0).toUpperCase() + message.slice(1) : "";
@@ -12858,7 +13585,7 @@ function useCameraPreview(deviceId2, effects) {
12858
13585
  current.current = null;
12859
13586
  setPreview(null);
12860
13587
  };
12861
- }, [deviceId2]);
13588
+ }, [deviceId2, attempt]);
12862
13589
  return {
12863
13590
  preview,
12864
13591
  error,
@@ -12881,9 +13608,9 @@ function DevicePicker({
12881
13608
  const triggerId = `mx-meet-device-${kind}`;
12882
13609
  const valueOf = (deviceId2) => deviceId2.length > 0 ? deviceId2 : "default";
12883
13610
  const current = devices?.selected[kind] ?? host.devices.resolve(kind) ?? "";
12884
- return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__field", children: [
13611
+ return /* @__PURE__ */ jsxs4("div", { className: "mx-meet__field", children: [
12885
13612
  /* @__PURE__ */ jsx6(Label, { htmlFor: triggerId, children: label }),
12886
- /* @__PURE__ */ jsxs3(
13613
+ /* @__PURE__ */ jsxs4(
12887
13614
  Select,
12888
13615
  {
12889
13616
  ...current.length > 0 ? { value: valueOf(current) } : {},
@@ -12970,19 +13697,19 @@ function MeetSkinRoom({
12970
13697
  if (screen === "refused" || screen === "exit") {
12971
13698
  return /* @__PURE__ */ jsx6("div", { className: "mx-meet mx-meet--lobby", children: /* @__PURE__ */ jsx6(ExitScreen, { ...onLeave !== void 0 ? { onLeave } : {} }) });
12972
13699
  }
12973
- return /* @__PURE__ */ jsxs3("div", { className: "mx-meet", children: [
13700
+ return /* @__PURE__ */ jsxs4("div", { className: "mx-meet", children: [
12974
13701
  /* @__PURE__ */ jsx6(ConsentNotice, {}),
12975
13702
  /* @__PURE__ */ jsx6(AttendanceNotice, {}),
12976
- /* @__PURE__ */ jsxs3("header", { className: "mx-meet__header", children: [
13703
+ /* @__PURE__ */ jsxs4("header", { className: "mx-meet__header", children: [
12977
13704
  /* @__PURE__ */ jsx6("h1", { className: "mx-meet__title", children: snapshot?.meeting?.title ?? "Meeting" }),
12978
- /* @__PURE__ */ jsxs3("span", { className: "mx-meet__meta", children: [
13705
+ /* @__PURE__ */ jsxs4("span", { className: "mx-meet__meta", children: [
12979
13706
  participantSummary(snapshot?.participants ?? []),
12980
13707
  elapsed > 0 ? ` \xB7 ${formatDurationMs3(elapsed)}` : ""
12981
13708
  ] }),
12982
13709
  /* @__PURE__ */ jsx6(RecordingIndicator, {}),
12983
13710
  captureHeldOf(snapshot?.meeting) ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__badge", "data-meet-capture-held": "", children: "Confidential \xB7 not recorded" }) : null,
12984
13711
  isObserverRole(snapshot?.grants.role) ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__badge", "data-meet-observing": "self", children: "Observing" }) : null,
12985
- snapshot?.locked === true ? /* @__PURE__ */ jsxs3("span", { className: "mx-meet__badge", children: [
13712
+ snapshot?.locked === true ? /* @__PURE__ */ jsxs4("span", { className: "mx-meet__badge", children: [
12986
13713
  /* @__PURE__ */ jsx6(LockIcon, { width: 12, height: 12 }),
12987
13714
  " Locked"
12988
13715
  ] }) : null,
@@ -12998,9 +13725,17 @@ function MeetSkinRoom({
12998
13725
  }
12999
13726
  function ShareNotice() {
13000
13727
  const { share, actions } = useMeetCore();
13728
+ const [soundHintClosed, setSoundHintClosed] = useState5(null);
13729
+ if (share.state === "presenting" && share.audio === false && soundHintClosed !== share.at) {
13730
+ return /* @__PURE__ */ jsxs4("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-audio-missing", children: [
13731
+ "Sound not shared",
13732
+ /* @__PURE__ */ jsx6("span", { className: "mx-meet__remedy", children: "To include sound, share a tab and turn on Also share tab audio" }),
13733
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: () => setSoundHintClosed(share.at), children: "Dismiss" })
13734
+ ] });
13735
+ }
13001
13736
  if (share.state !== "refused") return null;
13002
13737
  const text3 = share.reason === "unsupported" ? "You can't share your screen from this browser" : share.reason === "blocked" ? "Screen sharing is blocked" : "Couldn't start sharing your screen";
13003
- return /* @__PURE__ */ jsxs3("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-unavailable", children: [
13738
+ return /* @__PURE__ */ jsxs4("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-unavailable", children: [
13004
13739
  text3,
13005
13740
  share.remedy !== null ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__remedy", children: share.remedy }) : null,
13006
13741
  /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: actions.dismissShareNotice, children: "Dismiss" })
@@ -13028,7 +13763,7 @@ function clockText(ms2) {
13028
13763
  }
13029
13764
  function ExitScreen({ onLeave }) {
13030
13765
  const { phase, reason, host, snapshot, actions, call, connection } = useMeetCore();
13031
- const [busy, setBusy] = useState4(false);
13766
+ const [busy, setBusy] = useState5(false);
13032
13767
  const failure = snapshot?.failure ?? null;
13033
13768
  const retryLeft = useCountdown(phase === "refused:lobby_rejects" ? failure?.retryAt ?? null : null);
13034
13769
  const rejoin = /* @__PURE__ */ jsx6(
@@ -13069,7 +13804,7 @@ function ExitScreen({ onLeave }) {
13069
13804
  const code = phase.slice("refused:".length);
13070
13805
  title = REFUSED_TITLES[code] ?? "You can't join this meeting";
13071
13806
  line = code === "too_early" || code === "registration_required" || code === "cancelled" ? failure?.remedy ?? null : null;
13072
- actionsNode = /* @__PURE__ */ jsxs3(Fragment, { children: [
13807
+ actionsNode = /* @__PURE__ */ jsxs4(Fragment, { children: [
13073
13808
  /* @__PURE__ */ jsx6(Button, { type: "button", variant: "outline", onClick: actions.retry, children: "Try again" }),
13074
13809
  home
13075
13810
  ] });
@@ -13080,7 +13815,7 @@ function ExitScreen({ onLeave }) {
13080
13815
  actionsNode = home;
13081
13816
  } else if (retryLeft === 0) {
13082
13817
  line = "You can ask to join again";
13083
- actionsNode = /* @__PURE__ */ jsxs3(Fragment, { children: [
13818
+ actionsNode = /* @__PURE__ */ jsxs4(Fragment, { children: [
13084
13819
  /* @__PURE__ */ jsx6(
13085
13820
  Button,
13086
13821
  {
@@ -13104,16 +13839,37 @@ function ExitScreen({ onLeave }) {
13104
13839
  switch (phase) {
13105
13840
  case "left":
13106
13841
  title = "You left the meeting";
13107
- actionsNode = /* @__PURE__ */ jsxs3(Fragment, { children: [
13842
+ actionsNode = /* @__PURE__ */ jsxs4(Fragment, { children: [
13108
13843
  rejoin,
13109
13844
  home
13110
13845
  ] });
13111
13846
  break;
13112
13847
  case "disconnected":
13848
+ if (failure?.stage === "join" && (failure.reason ?? null) === null) {
13849
+ title = "Can't connect to the meeting";
13850
+ line = connection === "offline" ? "You're offline. Check your internet connection." : "Check your network. A VPN, firewall or proxy may be blocking the call.";
13851
+ notice = "cannot-connect";
13852
+ actionsNode = /* @__PURE__ */ jsxs4(Fragment, { children: [
13853
+ /* @__PURE__ */ jsx6(
13854
+ Button,
13855
+ {
13856
+ type: "button",
13857
+ disabled: busy,
13858
+ onClick: () => {
13859
+ setBusy(true);
13860
+ void actions.rejoin().catch(() => void 0).finally(() => setBusy(false));
13861
+ },
13862
+ children: "Try again"
13863
+ }
13864
+ ),
13865
+ home
13866
+ ] });
13867
+ break;
13868
+ }
13113
13869
  title = "You lost your connection to the meeting";
13114
13870
  line = connection === "offline" ? "You're offline. You'll rejoin automatically when your internet is back." : connection === "server_unreachable" ? "Can't reach the meeting server. You'll rejoin automatically when it answers." : failure?.remedy ?? "Check your internet connection, then rejoin.";
13115
13871
  notice = "disconnected";
13116
- actionsNode = /* @__PURE__ */ jsxs3(Fragment, { children: [
13872
+ actionsNode = /* @__PURE__ */ jsxs4(Fragment, { children: [
13117
13873
  rejoin,
13118
13874
  home
13119
13875
  ] });
@@ -13126,14 +13882,14 @@ function ExitScreen({ onLeave }) {
13126
13882
  title = "You joined from another tab or device";
13127
13883
  line = "This screen left the meeting so you aren't in it twice.";
13128
13884
  notice = "displaced";
13129
- actionsNode = /* @__PURE__ */ jsxs3(Fragment, { children: [
13885
+ actionsNode = /* @__PURE__ */ jsxs4(Fragment, { children: [
13130
13886
  rejoin,
13131
13887
  home
13132
13888
  ] });
13133
13889
  break;
13134
13890
  case "inactive":
13135
13891
  title = "You left the meeting because this tab was inactive";
13136
- actionsNode = /* @__PURE__ */ jsxs3(Fragment, { children: [
13892
+ actionsNode = /* @__PURE__ */ jsxs4(Fragment, { children: [
13137
13893
  rejoin,
13138
13894
  home
13139
13895
  ] });
@@ -13156,7 +13912,7 @@ function ExitScreen({ onLeave }) {
13156
13912
  break;
13157
13913
  }
13158
13914
  }
13159
- return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__waiting", "data-meet-exit": phase, children: [
13915
+ return /* @__PURE__ */ jsxs4("div", { className: "mx-meet__waiting", "data-meet-exit": phase, children: [
13160
13916
  /* @__PURE__ */ jsx6(Avatar, { name: host?.identity.displayName ?? "", url: host?.identity.avatarUrl ?? null, size: 72 }),
13161
13917
  /* @__PURE__ */ jsx6("h1", { className: "mx-meet__prejoin-title", children: title }),
13162
13918
  line !== null ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__empty-body", children: line }) : null,
@@ -13167,7 +13923,7 @@ function ExitScreen({ onLeave }) {
13167
13923
  function WaitingRoom({ host, onLeave }) {
13168
13924
  const { waiting, withdraw, askAgain } = useWaiting();
13169
13925
  const { phaseNotice } = useMeetCore();
13170
- const [busy, setBusy] = useState4(false);
13926
+ const [busy, setBusy] = useState5(false);
13171
13927
  const admission = waiting?.admission ?? "knocking";
13172
13928
  const reknockLeft = useCountdown(admission === "denied" ? waiting?.reknockAt ?? null : null);
13173
13929
  const run = (action) => () => {
@@ -13190,21 +13946,21 @@ function WaitingRoom({ host, onLeave }) {
13190
13946
  line = `You can ask again in ${clockText(reknockLeft)}`;
13191
13947
  cooldownNotice = true;
13192
13948
  }
13193
- actions = /* @__PURE__ */ jsxs3(Fragment, { children: [
13949
+ actions = /* @__PURE__ */ jsxs4(Fragment, { children: [
13194
13950
  waiting?.reknockAllowed === true || waiting?.reknockAt != null && reknockLeft === 0 ? /* @__PURE__ */ jsx6(Button, { type: "button", disabled: busy, onClick: run(askAgain), children: "Ask to join again" }) : null,
13195
13951
  home
13196
13952
  ] });
13197
13953
  break;
13198
13954
  case "expired":
13199
13955
  title = "No one responded";
13200
- actions = /* @__PURE__ */ jsxs3(Fragment, { children: [
13956
+ actions = /* @__PURE__ */ jsxs4(Fragment, { children: [
13201
13957
  /* @__PURE__ */ jsx6(Button, { type: "button", disabled: busy, onClick: run(askAgain), children: "Ask again" }),
13202
13958
  home
13203
13959
  ] });
13204
13960
  break;
13205
13961
  case "withdrawn":
13206
13962
  title = "You left the waiting room";
13207
- actions = /* @__PURE__ */ jsxs3(Fragment, { children: [
13963
+ actions = /* @__PURE__ */ jsxs4(Fragment, { children: [
13208
13964
  /* @__PURE__ */ jsx6(Button, { type: "button", disabled: busy, onClick: run(askAgain), children: "Rejoin" }),
13209
13965
  home
13210
13966
  ] });
@@ -13218,7 +13974,7 @@ function WaitingRoom({ host, onLeave }) {
13218
13974
  line = waiting?.admittersPresent === false ? "The host isn't here yet" : null;
13219
13975
  actions = /* @__PURE__ */ jsx6(Button, { type: "button", variant: "outline", disabled: busy, onClick: run(withdraw), children: "Cancel request" });
13220
13976
  }
13221
- return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__waiting", children: [
13977
+ return /* @__PURE__ */ jsxs4("div", { className: "mx-meet__waiting", children: [
13222
13978
  /* @__PURE__ */ jsx6(Avatar, { name: host.identity.displayName, url: host.identity.avatarUrl, size: 72 }),
13223
13979
  /* @__PURE__ */ jsx6("h1", { className: "mx-meet__prejoin-title", children: title }),
13224
13980
  phaseNotice === "sent-to-waiting" ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__empty-body", role: "status", "data-meet-notice": "sent-to-waiting", children: "The host moved you back to the waiting room" }) : null,
@@ -13238,9 +13994,9 @@ function HostMenu() {
13238
13994
  const host = useRequiredMeetHost();
13239
13995
  const snapshot = useMeetSnapshot();
13240
13996
  const end = useEndMeeting();
13241
- const [confirmingEnd, setConfirmingEnd] = useState4(false);
13997
+ const [confirmingEnd, setConfirmingEnd] = useState5(false);
13242
13998
  const locked = snapshot?.locked === true;
13243
- return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__host-menu", children: [
13999
+ return /* @__PURE__ */ jsxs4("div", { className: "mx-meet__host-menu", children: [
13244
14000
  /* @__PURE__ */ jsx6(
13245
14001
  "button",
13246
14002
  {
@@ -13252,7 +14008,7 @@ function HostMenu() {
13252
14008
  children: locked ? "Unlock meeting" : "Lock meeting"
13253
14009
  }
13254
14010
  ),
13255
- end.canEnd ? confirmingEnd ? /* @__PURE__ */ jsxs3("span", { className: "mx-meet__confirm", role: "group", "aria-label": "End this meeting?", children: [
14011
+ end.canEnd ? confirmingEnd ? /* @__PURE__ */ jsxs4("span", { className: "mx-meet__confirm", role: "group", "aria-label": "End this meeting?", children: [
13256
14012
  /* @__PURE__ */ jsx6("span", { className: "mx-meet__confirm-text", children: "End it for everyone? Every participant is disconnected, the note-taker stops, and the wrap-up is written. Nobody can rejoin this meeting afterwards." }),
13257
14013
  /* @__PURE__ */ jsx6(
13258
14014
  "button",
@@ -13308,9 +14064,9 @@ function GuestRecordAccessControl({
13308
14064
  const isHost = inRoomHost || viewerId !== null && meeting.hostUserId === viewerId;
13309
14065
  if (host === null || !isHost) return null;
13310
14066
  const resolved = access.setting === "default" && access.effective !== null ? ` Right now that means: ${guestRecordAccessLabel(access.effective.level).toLowerCase()}.` : "";
13311
- return /* @__PURE__ */ jsxs3("section", { className: "mx-meet__record-section", "aria-label": "What guests may read", children: [
14067
+ return /* @__PURE__ */ jsxs4("section", { className: "mx-meet__record-section", "aria-label": "What guests may read", children: [
13312
14068
  /* @__PURE__ */ jsx6("h2", { className: "mx-meet__record-section-title", children: "What guests may read" }),
13313
- /* @__PURE__ */ jsxs3("label", { className: "mx-meet__field", children: [
14069
+ /* @__PURE__ */ jsxs4("label", { className: "mx-meet__field", children: [
13314
14070
  /* @__PURE__ */ jsx6("span", { className: "mx-meet__field-label", children: "People who attended by link, without an account" }),
13315
14071
  /* @__PURE__ */ jsx6(
13316
14072
  "select",
@@ -13327,12 +14083,12 @@ function GuestRecordAccessControl({
13327
14083
  }
13328
14084
  )
13329
14085
  ] }),
13330
- /* @__PURE__ */ jsxs3("p", { className: "mx-meet__panel-note", children: [
14086
+ /* @__PURE__ */ jsxs4("p", { className: "mx-meet__panel-note", children: [
13331
14087
  guestRecordAccessExplanation(access.setting),
13332
14088
  resolved
13333
14089
  ] }),
13334
14090
  access.saving ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__panel-note", role: "status", children: "Saving what guests may read\u2026" }) : null,
13335
- access.error !== null ? /* @__PURE__ */ jsxs3(ErrorBox, { as: "div", className: "mx-meet__refusal", error: access.error, operation: "Change who can join", origin: "meet/components", children: [
14091
+ access.error !== null ? /* @__PURE__ */ jsxs4(ErrorBox, { as: "div", className: "mx-meet__refusal", error: access.error, operation: "Change who can join", origin: "meet/components", children: [
13336
14092
  /* @__PURE__ */ jsx6("p", { className: "mx-meet__refusal-reason", children: access.error.message }),
13337
14093
  /* @__PURE__ */ jsx6("p", { className: "mx-meet__refusal-remedy", children: access.error.remedy })
13338
14094
  ] }) : null
@@ -13340,9 +14096,9 @@ function GuestRecordAccessControl({
13340
14096
  }
13341
14097
 
13342
14098
  // src/react/record.tsx
13343
- import { useEffect as useEffect7, useState as useState5 } from "react";
14099
+ import { useEffect as useEffect7, useState as useState6 } from "react";
13344
14100
  import { ErrorBox as ErrorBox2 } from "@ai-matrx/design-system";
13345
- import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
14101
+ import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
13346
14102
  function MeetingRecordView({
13347
14103
  meeting,
13348
14104
  onRejoin
@@ -13359,17 +14115,17 @@ function MeetingRecordBody({
13359
14115
  }) {
13360
14116
  const host = useRequiredMeetHost();
13361
14117
  const record = useMeetingRecord(meeting);
13362
- const [question, setQuestion] = useState5("");
13363
- const [tab, setTab] = useState5("summary");
14118
+ const [question, setQuestion] = useState6("");
14119
+ const [tab, setTab] = useState6("summary");
13364
14120
  if (record.failure !== null) {
13365
- return /* @__PURE__ */ jsx7("div", { className: "mx-meet mx-meet--record", children: /* @__PURE__ */ jsxs4(ErrorBox2, { as: "div", className: "mx-meet__empty", error: record.failure, operation: "Open this meeting record", origin: "meet/components", children: [
14121
+ return /* @__PURE__ */ jsx7("div", { className: "mx-meet mx-meet--record", children: /* @__PURE__ */ jsxs5(ErrorBox2, { as: "div", className: "mx-meet__empty", error: record.failure, operation: "Open this meeting record", origin: "meet/components", children: [
13366
14122
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__empty-title", children: "This meeting record could not be opened" }),
13367
14123
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__empty-body", children: record.failure.message }),
13368
14124
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__empty-body", children: record.failure.remedy })
13369
14125
  ] }) });
13370
14126
  }
13371
14127
  if (record.loading || record.record === null) {
13372
- return /* @__PURE__ */ jsx7("div", { className: "mx-meet mx-meet--record", children: /* @__PURE__ */ jsxs4("div", { className: "mx-meet__empty", role: "status", children: [
14128
+ return /* @__PURE__ */ jsx7("div", { className: "mx-meet mx-meet--record", children: /* @__PURE__ */ jsxs5("div", { className: "mx-meet__empty", role: "status", children: [
13373
14129
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__empty-title", children: "Opening the meeting record\u2026" }),
13374
14130
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__empty-body", children: "Reading the summary, the notes and the transcript this meeting left behind." })
13375
14131
  ] }) });
@@ -13390,29 +14146,29 @@ function MeetingRecordBody({
13390
14146
  );
13391
14147
  const entirelyRestricted = everyRecordPartRefused && bundle.attendees.length === 0 && bundle.liveNotes.length === 0 && bundle.transcript.length === 0 && bundle.recording === null && bundle.summary === null && bundle.decisions.length === 0 && bundle.actionItems.length === 0;
13392
14148
  if (entirelyRestricted) {
13393
- return /* @__PURE__ */ jsxs4("div", { className: "mx-meet mx-meet--record", children: [
13394
- /* @__PURE__ */ jsx7("header", { className: "mx-meet__record-head", children: /* @__PURE__ */ jsxs4("div", { className: "mx-meet__record-head-inner", children: [
14149
+ return /* @__PURE__ */ jsxs5("div", { className: "mx-meet mx-meet--record", children: [
14150
+ /* @__PURE__ */ jsx7("header", { className: "mx-meet__record-head", children: /* @__PURE__ */ jsxs5("div", { className: "mx-meet__record-head-inner", children: [
13395
14151
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__record-eyebrow", children: "Meeting record" }),
13396
14152
  /* @__PURE__ */ jsx7("h1", { className: "mx-meet__record-title", children: meeting.title }),
13397
14153
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__record-when", children: meetingWhen(meeting) }),
13398
14154
  onRejoin !== void 0 ? /* @__PURE__ */ jsx7("button", { type: "button", className: "mx-meet__link", onClick: onRejoin, children: "Open this link again" }) : null
13399
14155
  ] }) }),
13400
- /* @__PURE__ */ jsx7("div", { className: "mx-meet__record-body", children: restrictionNotices.map((notice2) => /* @__PURE__ */ jsxs4("div", { className: "mx-meet__record-access", role: "status", children: [
14156
+ /* @__PURE__ */ jsx7("div", { className: "mx-meet__record-body", children: restrictionNotices.map((notice2) => /* @__PURE__ */ jsxs5("div", { className: "mx-meet__record-access", role: "status", children: [
13401
14157
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__record-access-title", children: "About this record" }),
13402
14158
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__record-access-message", children: notice2.message }),
13403
14159
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__record-access-remedy", children: notice2.remedy })
13404
14160
  ] }, notice2.parts.join("-"))) })
13405
14161
  ] });
13406
14162
  }
13407
- return /* @__PURE__ */ jsxs4("div", { className: "mx-meet mx-meet--record", children: [
13408
- /* @__PURE__ */ jsx7("header", { className: "mx-meet__record-head", children: /* @__PURE__ */ jsxs4("div", { className: "mx-meet__record-head-inner", children: [
14163
+ return /* @__PURE__ */ jsxs5("div", { className: "mx-meet mx-meet--record", children: [
14164
+ /* @__PURE__ */ jsx7("header", { className: "mx-meet__record-head", children: /* @__PURE__ */ jsxs5("div", { className: "mx-meet__record-head-inner", children: [
13409
14165
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__record-eyebrow", children: "Meeting record" }),
13410
14166
  /* @__PURE__ */ jsx7("h1", { className: "mx-meet__record-title", children: meeting.title }),
13411
14167
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__record-when", children: meetingWhen(meeting) }),
13412
14168
  onRejoin !== void 0 ? /* @__PURE__ */ jsx7("button", { type: "button", className: "mx-meet__link", onClick: onRejoin, children: "Open this link again" }) : null
13413
14169
  ] }) }),
13414
- /* @__PURE__ */ jsxs4("div", { className: "mx-meet__record-body", children: [
13415
- restrictionNotices.map((notice2) => /* @__PURE__ */ jsxs4("div", { className: "mx-meet__record-access", role: "status", children: [
14170
+ /* @__PURE__ */ jsxs5("div", { className: "mx-meet__record-body", children: [
14171
+ restrictionNotices.map((notice2) => /* @__PURE__ */ jsxs5("div", { className: "mx-meet__record-access", role: "status", children: [
13416
14172
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__record-access-title", children: "About this record" }),
13417
14173
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__record-access-message", children: notice2.message }),
13418
14174
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__record-access-remedy", children: notice2.remedy })
@@ -13420,13 +14176,13 @@ function MeetingRecordBody({
13420
14176
  attended.length === 0 ? (
13421
14177
  /* NOT SILENCE. A refused attendance read used to remove the whole
13422
14178
  section, so the reader could not tell "nobody" from "not shown". */
13423
- /* @__PURE__ */ jsxs4("section", { className: "mx-meet__record-section", "aria-label": "Who attended", children: [
14179
+ /* @__PURE__ */ jsxs5("section", { className: "mx-meet__record-section", "aria-label": "Who attended", children: [
13424
14180
  /* @__PURE__ */ jsx7("h2", { className: "mx-meet__record-section-title", children: "Who attended" }),
13425
14181
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__panel-note", children: absenceSentence(bundle, "attendance", "Nobody is recorded as having attended.") })
13426
14182
  ] })
13427
- ) : /* @__PURE__ */ jsxs4("section", { className: "mx-meet__record-section", "aria-label": "Who attended", children: [
14183
+ ) : /* @__PURE__ */ jsxs5("section", { className: "mx-meet__record-section", "aria-label": "Who attended", children: [
13428
14184
  /* @__PURE__ */ jsx7("h2", { className: "mx-meet__record-section-title", children: "Who attended" }),
13429
- /* @__PURE__ */ jsx7("ul", { className: "mx-meet__record-attendees", children: attended.map((person) => /* @__PURE__ */ jsxs4("li", { className: "mx-meet__record-attendee", children: [
14185
+ /* @__PURE__ */ jsx7("ul", { className: "mx-meet__record-attendees", children: attended.map((person) => /* @__PURE__ */ jsxs5("li", { className: "mx-meet__record-attendee", children: [
13430
14186
  /* @__PURE__ */ jsx7(
13431
14187
  Avatar,
13432
14188
  {
@@ -13435,7 +14191,7 @@ function MeetingRecordBody({
13435
14191
  size: 28
13436
14192
  }
13437
14193
  ),
13438
- /* @__PURE__ */ jsxs4("span", { className: "mx-meet__person-name", children: [
14194
+ /* @__PURE__ */ jsxs5("span", { className: "mx-meet__person-name", children: [
13439
14195
  displayNameFor(bundle.names, person.identity, person.displayName),
13440
14196
  person.role === "host" ? /* @__PURE__ */ jsx7("span", { className: "mx-meet__badge", children: "Host" }) : null,
13441
14197
  person.userId === null && !person.isAgent ? /* @__PURE__ */ jsx7("span", { className: "mx-meet__badge", children: "Guest" }) : null,
@@ -13443,17 +14199,17 @@ function MeetingRecordBody({
13443
14199
  ] })
13444
14200
  ] }, person.id)) })
13445
14201
  ] }),
13446
- bundle.recording !== null ? /* @__PURE__ */ jsxs4("section", { className: "mx-meet__record-section mx-meet__recording-card", "aria-label": "Recording", children: [
14202
+ bundle.recording !== null ? /* @__PURE__ */ jsxs5("section", { className: "mx-meet__record-section mx-meet__recording-card", "aria-label": "Recording", children: [
13447
14203
  /* @__PURE__ */ jsx7("h2", { className: "mx-meet__record-section-title", children: "Recording" }),
13448
- fileId !== null ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
14204
+ fileId !== null ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
13449
14205
  /* @__PURE__ */ jsx7(RecordingPlayer, { fileId }),
13450
14206
  notice !== null ? /* @__PURE__ */ jsx7("p", { className: "mx-meet__panel-note", children: notice }) : null
13451
14207
  ] }) : /* @__PURE__ */ jsx7("p", { className: "mx-meet__panel-note", children: notice ?? "This recording has no file." })
13452
- ] }) : wasRefused(bundle, "recording") ? /* @__PURE__ */ jsxs4("section", { className: "mx-meet__record-section", "aria-label": "Recording", children: [
14208
+ ] }) : wasRefused(bundle, "recording") ? /* @__PURE__ */ jsxs5("section", { className: "mx-meet__record-section", "aria-label": "Recording", children: [
13453
14209
  /* @__PURE__ */ jsx7("h2", { className: "mx-meet__record-section-title", children: "Recording" }),
13454
14210
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__panel-note", children: REFUSED_ABSENCE })
13455
14211
  ] }) : null,
13456
- /* @__PURE__ */ jsxs4("nav", { className: "mx-meet__record-tabs", "aria-label": "Meeting record sections", children: [
14212
+ /* @__PURE__ */ jsxs5("nav", { className: "mx-meet__record-tabs", "aria-label": "Meeting record sections", children: [
13457
14213
  /* @__PURE__ */ jsx7(
13458
14214
  "button",
13459
14215
  {
@@ -13485,10 +14241,10 @@ function MeetingRecordBody({
13485
14241
  }
13486
14242
  )
13487
14243
  ] }),
13488
- tab === "summary" ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
13489
- /* @__PURE__ */ jsxs4("section", { className: "mx-meet__record-section", "aria-label": "Summary", children: [
14244
+ tab === "summary" ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
14245
+ /* @__PURE__ */ jsxs5("section", { className: "mx-meet__record-section", "aria-label": "Summary", children: [
13490
14246
  /* @__PURE__ */ jsx7("h2", { className: "mx-meet__record-section-title", children: "Summary" }),
13491
- wrapUp.state === "written" ? /* @__PURE__ */ jsx7("p", { className: "mx-meet__record-summary", children: bundle.summary?.text }) : wrapUp.state === "in-progress" ? /* @__PURE__ */ jsxs4("div", { className: "mx-meet__panel-note", role: "status", children: [
14247
+ wrapUp.state === "written" ? /* @__PURE__ */ jsx7("p", { className: "mx-meet__record-summary", children: bundle.summary?.text }) : wrapUp.state === "in-progress" ? /* @__PURE__ */ jsxs5("div", { className: "mx-meet__panel-note", role: "status", children: [
13492
14248
  /* @__PURE__ */ jsx7("p", { children: wrapUp.message }),
13493
14249
  /* @__PURE__ */ jsx7("button", { type: "button", className: "mx-meet__link", onClick: record.reload, children: "Check again" })
13494
14250
  ] }) : /* @__PURE__ */ jsx7("p", { className: "mx-meet__panel-note", role: wrapUp.state === "failed" ? "alert" : void 0, children: wrapUp.message })
@@ -13518,22 +14274,22 @@ function MeetingRecordBody({
13518
14274
  }
13519
14275
  )
13520
14276
  ] }) : null,
13521
- tab === "notes" ? /* @__PURE__ */ jsxs4("section", { className: "mx-meet__record-section", "aria-label": "Notes as it happened", children: [
14277
+ tab === "notes" ? /* @__PURE__ */ jsxs5("section", { className: "mx-meet__record-section", "aria-label": "Notes as it happened", children: [
13522
14278
  /* @__PURE__ */ jsx7("h2", { className: "mx-meet__record-section-title", children: "Notes as it happened" }),
13523
14279
  bundle.liveNotes.length === 0 ? /* @__PURE__ */ jsx7("p", { className: "mx-meet__panel-note", children: absenceSentence(
13524
14280
  bundle,
13525
14281
  "notes",
13526
14282
  "No live notes are available for this meeting."
13527
- ) }) : /* @__PURE__ */ jsx7("ul", { className: "mx-meet__notes", children: bundle.liveNotes.map((note) => /* @__PURE__ */ jsxs4("li", { className: "mx-meet__note", children: [
13528
- note.window !== null ? /* @__PURE__ */ jsxs4("span", { className: "mx-meet__badge", children: [
14283
+ ) }) : /* @__PURE__ */ jsx7("ul", { className: "mx-meet__notes", children: bundle.liveNotes.map((note) => /* @__PURE__ */ jsxs5("li", { className: "mx-meet__note", children: [
14284
+ note.window !== null ? /* @__PURE__ */ jsxs5("span", { className: "mx-meet__badge", children: [
13529
14285
  "Window ",
13530
14286
  note.window
13531
14287
  ] }) : null,
13532
14288
  note.text
13533
14289
  ] }, note.id)) })
13534
14290
  ] }) : null,
13535
- tab === "transcript" ? /* @__PURE__ */ jsxs4("section", { className: "mx-meet__record-section", "aria-label": "Transcript", children: [
13536
- /* @__PURE__ */ jsxs4("h2", { className: "mx-meet__record-section-title", children: [
14291
+ tab === "transcript" ? /* @__PURE__ */ jsxs5("section", { className: "mx-meet__record-section", "aria-label": "Transcript", children: [
14292
+ /* @__PURE__ */ jsxs5("h2", { className: "mx-meet__record-section-title", children: [
13537
14293
  "Transcript",
13538
14294
  /* @__PURE__ */ jsx7("span", { className: "mx-meet__record-count", children: bundle.transcript.length === 1 ? "1 line" : `${bundle.transcript.length} lines` })
13539
14295
  ] }),
@@ -13541,8 +14297,8 @@ function MeetingRecordBody({
13541
14297
  bundle,
13542
14298
  "transcript",
13543
14299
  "No transcript is available for this meeting."
13544
- ) }) : /* @__PURE__ */ jsx7("ol", { className: "mx-meet__record-transcript", children: blocks.map((block) => /* @__PURE__ */ jsxs4("li", { className: "mx-meet__record-block", children: [
13545
- /* @__PURE__ */ jsxs4("p", { className: "mx-meet__record-speaker", children: [
14300
+ ) }) : /* @__PURE__ */ jsx7("ol", { className: "mx-meet__record-transcript", children: blocks.map((block) => /* @__PURE__ */ jsxs5("li", { className: "mx-meet__record-block", children: [
14301
+ /* @__PURE__ */ jsxs5("p", { className: "mx-meet__record-speaker", children: [
13546
14302
  block.speaker,
13547
14303
  /* @__PURE__ */ jsx7("time", { className: "mx-meet__record-time", dateTime: block.startedAt, children: clockOf(block.startedAt) })
13548
14304
  ] }),
@@ -13550,10 +14306,10 @@ function MeetingRecordBody({
13550
14306
  ] }, block.id)) })
13551
14307
  ] }) : null,
13552
14308
  /* @__PURE__ */ jsx7(GuestRecordAccessControl, { meeting }),
13553
- /* @__PURE__ */ jsxs4("section", { className: "mx-meet__record-section", "aria-label": "Ask about this meeting", children: [
14309
+ /* @__PURE__ */ jsxs5("section", { className: "mx-meet__record-section", "aria-label": "Ask about this meeting", children: [
13554
14310
  /* @__PURE__ */ jsx7("h2", { className: "mx-meet__record-section-title", children: "Ask about this meeting" }),
13555
- guestFinishedMeeting ? /* @__PURE__ */ jsx7("p", { className: "mx-meet__panel-note", children: "Sign in to ask the assistant about a meeting after it ends." }) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
13556
- /* @__PURE__ */ jsxs4(
14311
+ guestFinishedMeeting ? /* @__PURE__ */ jsx7("p", { className: "mx-meet__panel-note", children: "Sign in to ask the assistant about a meeting after it ends." }) : /* @__PURE__ */ jsxs5(Fragment2, { children: [
14312
+ /* @__PURE__ */ jsxs5(
13557
14313
  "form",
13558
14314
  {
13559
14315
  className: "mx-meet__composer",
@@ -13587,7 +14343,7 @@ function MeetingRecordBody({
13587
14343
  }
13588
14344
  ),
13589
14345
  record.answer !== null ? /* @__PURE__ */ jsx7("p", { className: "mx-meet__answer", children: record.answer }) : null,
13590
- record.coverage !== null ? /* @__PURE__ */ jsxs4("p", { className: "mx-meet__coverage", children: [
14346
+ record.coverage !== null ? /* @__PURE__ */ jsxs5("p", { className: "mx-meet__coverage", children: [
13591
14347
  "Answered from ",
13592
14348
  record.coverage.segmentsUsed,
13593
14349
  " of ",
@@ -13598,7 +14354,7 @@ function MeetingRecordBody({
13598
14354
  record.coverage.truncated ? " (the oldest were left out)" : "",
13599
14355
  "."
13600
14356
  ] }) : null,
13601
- record.askError !== null ? /* @__PURE__ */ jsxs4(ErrorBox2, { as: "div", className: "mx-meet__refusal", error: record.askError, operation: "Ask about this meeting", origin: "meet/components", children: [
14357
+ record.askError !== null ? /* @__PURE__ */ jsxs5(ErrorBox2, { as: "div", className: "mx-meet__refusal", error: record.askError, operation: "Ask about this meeting", origin: "meet/components", children: [
13602
14358
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__refusal-reason", children: record.askError.message }),
13603
14359
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__refusal-remedy", children: record.askError.remedy })
13604
14360
  ] }) : null
@@ -13615,16 +14371,16 @@ function RecordingPlayer(props) {
13615
14371
  return /* @__PURE__ */ jsx7(RecordingPlayerBody, { ...props, host });
13616
14372
  }
13617
14373
  function RecordingPlayerBody({ fileId, host }) {
13618
- const [media, setMedia] = useState5(null);
13619
- const [loading, setLoading] = useState5(false);
13620
- const [error, setError] = useState5(null);
14374
+ const [media, setMedia] = useState6(null);
14375
+ const [loading, setLoading] = useState6(false);
14376
+ const [error, setError] = useState6(null);
13621
14377
  useEffect7(() => {
13622
14378
  if (media === null || media.kind !== "bytes") return void 0;
13623
14379
  const url = media.url;
13624
14380
  return () => URL.revokeObjectURL(url);
13625
14381
  }, [media]);
13626
14382
  if (media !== null) {
13627
- return /* @__PURE__ */ jsxs4("div", { className: "mx-meet__record-section", children: [
14383
+ return /* @__PURE__ */ jsxs5("div", { className: "mx-meet__record-section", children: [
13628
14384
  /* @__PURE__ */ jsx7(
13629
14385
  "video",
13630
14386
  {
@@ -13670,8 +14426,8 @@ function RecordingPlayerBody({ fileId, host }) {
13670
14426
  media.fallbackReason !== null ? /* @__PURE__ */ jsx7("p", { className: "mx-meet__panel-note", children: media.fallbackReason }) : null
13671
14427
  ] });
13672
14428
  }
13673
- return /* @__PURE__ */ jsxs4("div", { className: "mx-meet__record-section", children: [
13674
- error !== null ? /* @__PURE__ */ jsxs4(ErrorBox2, { as: "div", className: "mx-meet__refusal", error, operation: "Complete this meeting action", origin: "meet/components", children: [
14429
+ return /* @__PURE__ */ jsxs5("div", { className: "mx-meet__record-section", children: [
14430
+ error !== null ? /* @__PURE__ */ jsxs5(ErrorBox2, { as: "div", className: "mx-meet__refusal", error, operation: "Complete this meeting action", origin: "meet/components", children: [
13675
14431
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__refusal-reason", children: error.message }),
13676
14432
  /* @__PURE__ */ jsx7("p", { className: "mx-meet__refusal-remedy", children: error.remedy })
13677
14433
  ] }) : null,
@@ -13797,7 +14553,7 @@ function linkRunWords(run) {
13797
14553
 
13798
14554
  // src/react/calls-ui.tsx
13799
14555
  import { useEffect as useEffect9 } from "react";
13800
- import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
14556
+ import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
13801
14557
  function IncomingCallHost() {
13802
14558
  const calls = useCalls();
13803
14559
  const { call, phase, actions } = useMeetCore();
@@ -13811,9 +14567,9 @@ function IncomingCallHost() {
13811
14567
  return () => clearTimeout(timer);
13812
14568
  }, [outcome, actions]);
13813
14569
  const inCall = call !== null && (phase === "joining" || phase === "in_call" || phase === "disconnected" || phase === "removed" || phase === "superseded");
13814
- return /* @__PURE__ */ jsxs5(Fragment3, { children: [
14570
+ return /* @__PURE__ */ jsxs6(Fragment3, { children: [
13815
14571
  inCall && call !== null ? /* @__PURE__ */ jsx9("div", { className: "mx-meet__call-overlay", "data-meet-call": call.invite.id, children: /* @__PURE__ */ jsx9(MeetRoot, { children: /* @__PURE__ */ jsx9(MeetSkinRoom, { roomName: call.invite.roomName, onLeave: actions.dismissCall }) }) }) : null,
13816
- calls.incoming.length > 0 || call !== null && (phase === "ringing_out" || outcome) ? /* @__PURE__ */ jsxs5("div", { className: "mx-meet__ringing-stack", children: [
14572
+ calls.incoming.length > 0 || call !== null && (phase === "ringing_out" || outcome) ? /* @__PURE__ */ jsxs6("div", { className: "mx-meet__ringing-stack", children: [
13817
14573
  call !== null && phase === "ringing_out" ? /* @__PURE__ */ jsx9(OutgoingCallCard, { invite: call.invite }) : null,
13818
14574
  outcome && call !== null ? /* @__PURE__ */ jsx9(CallOutcomeCard, { invite: call.invite }) : null,
13819
14575
  calls.incoming.map((invite) => /* @__PURE__ */ jsx9(IncomingCallCard, { invite }, invite.id))
@@ -13822,9 +14578,9 @@ function IncomingCallHost() {
13822
14578
  }
13823
14579
  function OutgoingCallCard({ invite }) {
13824
14580
  const calls = useCalls();
13825
- return /* @__PURE__ */ jsxs5("section", { className: "mx-meet__ringing", role: "status", "aria-label": "Calling", "data-meet-notice": "ringing-out", children: [
14581
+ return /* @__PURE__ */ jsxs6("section", { className: "mx-meet__ringing", role: "status", "aria-label": "Calling", "data-meet-notice": "ringing-out", children: [
13826
14582
  /* @__PURE__ */ jsx9(Avatar, { name: invite.callerName, url: invite.callerAvatarUrl, size: 56 }),
13827
- /* @__PURE__ */ jsxs5("div", { className: "mx-meet__ringing-text", children: [
14583
+ /* @__PURE__ */ jsxs6("div", { className: "mx-meet__ringing-text", children: [
13828
14584
  /* @__PURE__ */ jsx9("strong", { children: "Calling\u2026" }),
13829
14585
  /* @__PURE__ */ jsx9("span", { children: invite.mode === "video" ? "Video call" : "Voice call" })
13830
14586
  ] }),
@@ -13835,20 +14591,20 @@ function CallOutcomeCard({ invite }) {
13835
14591
  const { phase, actions, call } = useMeetCore();
13836
14592
  const outgoing = call?.direction === "outgoing";
13837
14593
  const text3 = phase === "call_declined" ? "Call declined" : phase === "call_busy" ? "They're on another call" : phase === "answered_elsewhere" ? "Answered on another device" : outgoing ? "No answer" : `Missed call from ${invite.callerName}`;
13838
- return /* @__PURE__ */ jsxs5("section", { className: "mx-meet__ringing", role: "status", "aria-label": text3, "data-meet-call-outcome": phase, children: [
14594
+ return /* @__PURE__ */ jsxs6("section", { className: "mx-meet__ringing", role: "status", "aria-label": text3, "data-meet-call-outcome": phase, children: [
13839
14595
  /* @__PURE__ */ jsx9("div", { className: "mx-meet__ringing-text", children: /* @__PURE__ */ jsx9("strong", { children: text3 }) }),
13840
14596
  /* @__PURE__ */ jsx9("div", { className: "mx-meet__ringing-actions", children: /* @__PURE__ */ jsx9(ControlButton, { label: "Close", onClick: actions.dismissCall, children: /* @__PURE__ */ jsx9(PhoneOffIcon, {}) }) })
13841
14597
  ] });
13842
14598
  }
13843
14599
  function IncomingCallCard({ invite }) {
13844
14600
  const calls = useCalls();
13845
- return /* @__PURE__ */ jsxs5("section", { className: "mx-meet__ringing", role: "alertdialog", "aria-label": "Incoming call", children: [
14601
+ return /* @__PURE__ */ jsxs6("section", { className: "mx-meet__ringing", role: "alertdialog", "aria-label": "Incoming call", children: [
13846
14602
  /* @__PURE__ */ jsx9(Avatar, { name: invite.callerName, url: invite.callerAvatarUrl, size: 56 }),
13847
- /* @__PURE__ */ jsxs5("div", { className: "mx-meet__ringing-text", children: [
14603
+ /* @__PURE__ */ jsxs6("div", { className: "mx-meet__ringing-text", children: [
13848
14604
  /* @__PURE__ */ jsx9("strong", { children: invite.callerName }),
13849
14605
  /* @__PURE__ */ jsx9("span", { children: invite.mode === "video" ? "Incoming video call" : "Incoming call" })
13850
14606
  ] }),
13851
- /* @__PURE__ */ jsxs5("div", { className: "mx-meet__ringing-actions", children: [
14607
+ /* @__PURE__ */ jsxs6("div", { className: "mx-meet__ringing-actions", children: [
13852
14608
  /* @__PURE__ */ jsx9(ControlButton, { label: "Decline", danger: true, onClick: () => void calls.decline(invite), children: /* @__PURE__ */ jsx9(PhoneOffIcon, {}) }),
13853
14609
  invite.mode === "video" ? /* @__PURE__ */ jsx9(
13854
14610
  ControlButton,
@@ -14104,6 +14860,7 @@ export {
14104
14860
  autoAssign,
14105
14861
  autoRejoinDelayMs,
14106
14862
  availableActivities,
14863
+ breakoutClosingMs,
14107
14864
  breakoutRemainingMs,
14108
14865
  callInviteAction,
14109
14866
  callInviteText,
@@ -14139,6 +14896,7 @@ export {
14139
14896
  createReconnectPolicy,
14140
14897
  createRecordingController,
14141
14898
  createRoomEngine,
14899
+ createSpeakingWhileMutedDetector,
14142
14900
  createTokenClient,
14143
14901
  createWebDeviceStorage,
14144
14902
  createWebEffectsStorage,
@@ -14147,6 +14905,7 @@ export {
14147
14905
  createWebRoomTokenStorage,
14148
14906
  defaultCaptionLanguage,
14149
14907
  detectBrowser,
14908
+ detectCapabilities,
14150
14909
  detectOs,
14151
14910
  deviceChipLabel,
14152
14911
  deviceFixSteps,
@@ -14171,6 +14930,7 @@ export {
14171
14930
  isLinkFollower,
14172
14931
  isListenOnly,
14173
14932
  isLivePhase,
14933
+ isMobileUserAgent,
14174
14934
  isNoteTaker,
14175
14935
  isObserverRole,
14176
14936
  isRealName,
@@ -14263,6 +15023,7 @@ export {
14263
15023
  transcriptDocument,
14264
15024
  unavailableFrom,
14265
15025
  uniqueAttendees,
15026
+ unsupportedWords,
14266
15027
  untranslatedLines,
14267
15028
  useActivityAttention,
14268
15029
  useAudioLevel,
@@ -14271,6 +15032,7 @@ export {
14271
15032
  useCanModerate,
14272
15033
  useCanRecord,
14273
15034
  useCaptionTranslation,
15035
+ useCaptions,
14274
15036
  useCollab,
14275
15037
  useCollabPanelRequest,
14276
15038
  useConsentNotice,