@ai-matrx/meet 0.11.46 → 0.11.48

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.
@@ -1,6 +1,6 @@
1
- export { A as AiPanel, a as AttendanceNotice, b as Avatar, C as Captions, c as ChatPanel, d as ConnectionDot, e as ConsentNotice, f as ControlBar, g as ControlButton, H as HostMenu, L as LobbyPanel, M as MeetSkinRoom, h as MeetingOverlays, i as MenuItem, P as PanelShell, j as ParticipantTile, k as PeoplePanel, l as PersonMenu, m as Popover, n as PreJoin, Q as QualityBars, R as ReactionOverlay, o as RecordingIndicator, S as SettingsPanel, p as ShortcutsSheet, q as Stage, T as Toast } from '../components-CkyDos0f.cjs';
1
+ export { A as AiPanel, a as AttendanceNotice, b as Avatar, C as Captions, c as ChatPanel, d as ConnectionDot, e as ConsentNotice, f as ControlBar, g as ControlButton, H as HostMenu, L as LobbyPanel, M as MeetSkinRoom, h as MeetingOverlays, i as MenuItem, P as PanelShell, j as ParticipantTile, k as PeoplePanel, l as PersonMenu, m as Popover, n as PreJoin, Q as QualityBars, R as ReactionOverlay, o as RecordingIndicator, S as SettingsPanel, p as ShortcutsSheet, q as Stage, T as Toast } from '../components-fpQrqtcs.cjs';
2
2
  import { ReactNode, SVGProps } from 'react';
3
- import { C as CollabPanel } from '../collab-D1TX5uf9.cjs';
3
+ import { C as CollabPanel } from '../collab-Dk3r37MW.cjs';
4
4
 
5
5
  /**
6
6
  * POLLS, Q&A, THE SHARED WHITEBOARD AND BREAKOUT ROOMS — the in-room surfaces (Meet wave 5).
@@ -1,6 +1,6 @@
1
- export { A as AiPanel, a as AttendanceNotice, b as Avatar, C as Captions, c as ChatPanel, d as ConnectionDot, e as ConsentNotice, f as ControlBar, g as ControlButton, H as HostMenu, L as LobbyPanel, M as MeetSkinRoom, h as MeetingOverlays, i as MenuItem, P as PanelShell, j as ParticipantTile, k as PeoplePanel, l as PersonMenu, m as Popover, n as PreJoin, Q as QualityBars, R as ReactionOverlay, o as RecordingIndicator, S as SettingsPanel, p as ShortcutsSheet, q as Stage, T as Toast } from '../components-CNwBgmYU.js';
1
+ export { A as AiPanel, a as AttendanceNotice, b as Avatar, C as Captions, c as ChatPanel, d as ConnectionDot, e as ConsentNotice, f as ControlBar, g as ControlButton, H as HostMenu, L as LobbyPanel, M as MeetSkinRoom, h as MeetingOverlays, i as MenuItem, P as PanelShell, j as ParticipantTile, k as PeoplePanel, l as PersonMenu, m as Popover, n as PreJoin, Q as QualityBars, R as ReactionOverlay, o as RecordingIndicator, S as SettingsPanel, p as ShortcutsSheet, q as Stage, T as Toast } from '../components-BWqIoc_V.js';
2
2
  import { ReactNode, SVGProps } from 'react';
3
- import { C as CollabPanel } from '../collab-D1TX5uf9.js';
3
+ import { C as CollabPanel } from '../collab-Dk3r37MW.js';
4
4
 
5
5
  /**
6
6
  * POLLS, Q&A, THE SHARED WHITEBOARD AND BREAKOUT ROOMS — the in-room surfaces (Meet wave 5).
@@ -286,17 +286,34 @@ function recordingLabel(state) {
286
286
 
287
287
  // src/core/live-policy.ts
288
288
  var DEFAULT_LIVE_POLICY = {
289
+ headlessPowers: "members_admit",
289
290
  chatMode: "everyone",
290
291
  avLock: "off",
291
292
  timeLimitMinutes: 0,
292
293
  timeLimitWarningMinutes: 5,
293
- recordingConsent: "notice"
294
+ recordingConsent: "notice",
295
+ shareWhoCan: "everyone",
296
+ shareConcurrency: "one_takeover"
294
297
  };
295
298
  var MODERATORS = /* @__PURE__ */ new Set(["host", "cohost"]);
296
299
  function isLockedFor(policy, role, kind) {
297
300
  if (MODERATORS.has(role)) return false;
298
301
  return policy.avLock === "both" || (kind === "microphone" ? policy.avLock === "mic" : policy.avLock === "camera");
299
302
  }
303
+ function canShareUnder(policy, role, standing) {
304
+ if (role === "viewer") return false;
305
+ if (MODERATORS.has(role)) return true;
306
+ switch (policy.shareWhoCan) {
307
+ case "host_only":
308
+ return false;
309
+ case "presenters":
310
+ return role === "presenter";
311
+ case "org":
312
+ return role === "presenter" || standing === "member";
313
+ default:
314
+ return true;
315
+ }
316
+ }
300
317
  function consentPending(policy, role, recordingOn, given) {
301
318
  return recordingOn && !given && policy.recordingConsent !== "notice" && !MODERATORS.has(role);
302
319
  }
@@ -308,6 +325,12 @@ function minutesLeft(policy, startedAtMs, nowMs) {
308
325
  if (policy.timeLimitMinutes <= 0 || startedAtMs === null) return null;
309
326
  return Math.ceil((startedAtMs + policy.timeLimitMinutes * 6e4 - nowMs) / 6e4);
310
327
  }
328
+ function mayAdmitNow(role, participants, policy) {
329
+ if (role === "none") return false;
330
+ if (role === "host" || role === "cohost") return true;
331
+ const hostIn = participants.some((p) => p.role === "host" || p.role === "cohost");
332
+ return !hostIn && policy.headlessPowers === "members_admit";
333
+ }
311
334
 
312
335
  // src/core/attendance.ts
313
336
  var IDLE_PREVIEW = { state: "idle", unavailable: null };
@@ -645,7 +668,7 @@ function observe(input) {
645
668
  const phase = observedPhase(input);
646
669
  const inCall = phase === "in_call";
647
670
  const actual = inCall && snapshot !== null ? effectiveRole(snapshot) : "none";
648
- const canAdmit = actual !== "none" && ADMITTER_ROLES.has(actual);
671
+ const canAdmit = snapshot !== null && mayAdmitNow(actual, snapshot.participants, snapshot.livePolicy);
649
672
  const role = actual === "none" || snapshot === null ? "none" : observedRole(actual, effectiveStanding(snapshot));
650
673
  const deviceInPlay = phase === "joining" || inCall;
651
674
  const region = snapshot?.mediaRegion;
@@ -1019,6 +1042,7 @@ async function moveToRoom(runtime, roomId) {
1019
1042
  if (runtime === null) return { ok: false, message: "This meeting is not connected." };
1020
1043
  try {
1021
1044
  await runtime.engine.moveToBreakout(roomId);
1045
+ await runtime.inRoom.loadCollab?.().catch(() => void 0);
1022
1046
  return { ok: true, message: roomId === null ? "Back in the main room." : "Joined the breakout room." };
1023
1047
  } catch (error) {
1024
1048
  return { ok: false, message: error.message ?? "That room could not be joined." };
@@ -1188,6 +1212,13 @@ function requireSession(session, action) {
1188
1212
  if (session === null) throw noRuntime(action);
1189
1213
  return session;
1190
1214
  }
1215
+ function otherPresenterOf(snapshot) {
1216
+ if (snapshot === null || snapshot.presenter === null || snapshot.presenter === snapshot.localIdentity) return null;
1217
+ const rule = snapshot.livePolicy.shareConcurrency;
1218
+ if (rule === "multiple") return null;
1219
+ const who = snapshot.participants.find((p) => p.identity === snapshot.presenter);
1220
+ return who === void 0 ? null : { name: who.displayName, rule };
1221
+ }
1191
1222
  function useRoom() {
1192
1223
  const host = useMeetHost();
1193
1224
  const snapshot = useMeetSnapshot();
@@ -1202,7 +1233,9 @@ function useRoom() {
1202
1233
  secondDevice: snapshot?.secondDevice ?? null,
1203
1234
  local,
1204
1235
  // An observer (role `viewer`) publishes nothing: no Share screen to press and be refused (MD-16).
1205
- canShareScreen: (engine?.supportsScreenShare() ?? true) && !isObserverRole(snapshot?.grants.role ?? null),
1236
+ sharingAllowed: snapshot === null || canShareUnder(snapshot.livePolicy, effectiveRole(snapshot), effectiveStanding(snapshot)),
1237
+ canShareScreen: (engine?.supportsScreenShare() ?? true) && !isObserverRole(snapshot?.grants.role ?? null) && (snapshot === null || canShareUnder(snapshot.livePolicy, effectiveRole(snapshot), effectiveStanding(snapshot))),
1238
+ otherPresenter: otherPresenterOf(snapshot),
1206
1239
  join: useCallback2(
1207
1240
  async (request) => requireEngine(engine, "join").join(request),
1208
1241
  [engine]
@@ -1300,7 +1333,7 @@ function useLobby() {
1300
1333
  declined: (snapshot?.lobby ?? []).filter((entry) => entry.state === "denied"),
1301
1334
  // No runtime means no host powers — `<LobbyPanel>` renders nothing rather
1302
1335
  // than an admit button that would throw.
1303
- canAdmit: session !== null && isHost,
1336
+ canAdmit: session !== null && (isHost || snapshot !== null && snapshot.phase === "in_call" && mayAdmitNow(snapshot.participants.find((p) => p.identity === snapshot.localIdentity)?.role ?? "none", snapshot.participants, snapshot.livePolicy)),
1304
1337
  admit: useCallback2(
1305
1338
  async (entry) => requireSession(session, "admit").admit(entry, true),
1306
1339
  [session]
@@ -3414,7 +3447,8 @@ function ParticipantTile({
3414
3447
  participant.isLocal ? " (you)" : "",
3415
3448
  participant.isAgent ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__badge", children: "AI" }) : null,
3416
3449
  /* @__PURE__ */ jsx6(RoleBadge, { role: participant.role, standing: participant.standing }),
3417
- participant.reconnecting === true ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__badge", children: "Reconnecting\u2026" }) : null
3450
+ participant.reconnecting === true ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__badge", children: "Reconnecting\u2026" }) : null,
3451
+ participant.reconnecting !== true && participant.videoPaused === true && !participant.isLocal ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__badge", "data-meet-video-paused": "", children: "Video paused to save audio" }) : null
3418
3452
  ] }),
3419
3453
  !participant.micEnabled ? /* @__PURE__ */ jsx6(MicOffIcon, { width: 14, height: 14 }) : null,
3420
3454
  /* @__PURE__ */ jsx6(QualityBars, { quality: participant.quality })
@@ -3504,6 +3538,8 @@ function ControlBar({ onLeave }) {
3504
3538
  const queue = useHandQueue();
3505
3539
  const micUnavailable = useLocalMedia().microphone.unavailable;
3506
3540
  const live = useLivePolicy();
3541
+ const core = useMeetCore();
3542
+ const [askPresentInstead, setAskPresentInstead] = useState5(false);
3507
3543
  const unread = useUnreadChat(panel === "chat");
3508
3544
  const [whiteboard, setWhiteboard] = useState5(false);
3509
3545
  const app = useMeetAppPanel();
@@ -3592,15 +3628,39 @@ function ControlBar({ onLeave }) {
3592
3628
  }
3593
3629
  ),
3594
3630
  /* @__PURE__ */ jsx6(CaptionsControl, {}),
3595
- room.canShareScreen || room.local?.screenSharing === true ? /* @__PURE__ */ jsx6(
3596
- ControlButton,
3597
- {
3598
- label: room.local?.screenSharing === true ? "Stop sharing" : "Share screen",
3599
- active: room.local?.screenSharing === true,
3600
- onClick: () => void room.toggleScreenShare(),
3601
- children: /* @__PURE__ */ jsx6(ScreenShareIcon, {})
3602
- }
3603
- ) : null,
3631
+ room.canShareScreen || room.local?.screenSharing === true && core.share.state !== "ended" ? /* @__PURE__ */ jsxs4("span", { className: "mx-meet__anchor", children: [
3632
+ /* @__PURE__ */ jsx6(
3633
+ ControlButton,
3634
+ {
3635
+ label: room.local?.screenSharing === true ? "Stop sharing" : "Share screen",
3636
+ active: room.local?.screenSharing === true,
3637
+ onClick: () => {
3638
+ if (room.local?.screenSharing !== true && room.otherPresenter !== null) setAskPresentInstead(true);
3639
+ else void room.toggleScreenShare();
3640
+ },
3641
+ children: /* @__PURE__ */ jsx6(ScreenShareIcon, {})
3642
+ }
3643
+ ),
3644
+ askPresentInstead && room.otherPresenter !== null ? /* @__PURE__ */ jsxs4("div", { className: "mx-meet__confirm", role: "group", "aria-label": "Someone else is presenting", "data-meet-notice": "share-takeover", children: [
3645
+ /* @__PURE__ */ jsxs4("span", { className: "mx-meet__confirm-text", children: [
3646
+ room.otherPresenter.name,
3647
+ " is presenting"
3648
+ ] }),
3649
+ /* @__PURE__ */ jsx6(
3650
+ "button",
3651
+ {
3652
+ type: "button",
3653
+ className: "mx-meet__primary mx-meet__primary--small",
3654
+ onClick: () => {
3655
+ setAskPresentInstead(false);
3656
+ void room.toggleScreenShare();
3657
+ },
3658
+ children: room.otherPresenter.rule === "one_takeover" ? "Present instead" : "Share anyway"
3659
+ }
3660
+ ),
3661
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: () => setAskPresentInstead(false), children: "Cancel" })
3662
+ ] }) : null
3663
+ ] }) : null,
3604
3664
  /* @__PURE__ */ jsxs4("span", { className: "mx-meet__anchor", children: [
3605
3665
  /* @__PURE__ */ jsx6(
3606
3666
  ControlButton,
@@ -3697,7 +3757,7 @@ function ControlBar({ onLeave }) {
3697
3757
  ]
3698
3758
  }
3699
3759
  ),
3700
- !room.canShareScreen && room.local?.screenSharing !== true ? (
3760
+ !room.canShareScreen && room.sharingAllowed && room.local?.screenSharing !== true ? (
3701
3761
  // `share-unsupported-mobile`: the control is absent; the menu says where it lives.
3702
3762
  /* @__PURE__ */ jsxs4("p", { className: "mx-meet__menu-note", role: "note", "data-meet-notice": "share-unsupported", children: [
3703
3763
  /* @__PURE__ */ jsx6(ScreenShareIcon, { width: 16, height: 16 }),
@@ -5702,13 +5762,53 @@ function MeetSkinRoom({
5702
5762
  ] }),
5703
5763
  core.connectionBanner && core.connection !== "stable" ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": `connection-${core.connection}`, children: CONNECTION_BANNER[core.connection] }) : null,
5704
5764
  /* @__PURE__ */ jsx6(ShareNotice, {}),
5765
+ /* @__PURE__ */ jsx6(NetworkNotice, {}),
5705
5766
  /* @__PURE__ */ jsx6(Stage, {}),
5706
5767
  /* @__PURE__ */ jsx6(Captions, {}),
5707
5768
  /* @__PURE__ */ jsx6(ControlBar, { ...onLeave !== void 0 ? { onLeave } : {} })
5708
5769
  ] });
5709
5770
  }
5771
+ var RESTRICTED_NOTICE_MS = 3e4;
5772
+ function NetworkNotice() {
5773
+ const { snapshot, connectionBanner } = useMeetCore();
5774
+ const room = useRoom();
5775
+ const network = snapshot?.localNetwork;
5776
+ const [closed, setClosed] = useState5(() => /* @__PURE__ */ new Set());
5777
+ const [restrictedSince, setRestrictedSince] = useState5(null);
5778
+ const [, setTick] = useState5(0);
5779
+ const restricted = network?.path === "restricted";
5780
+ useEffect6(() => {
5781
+ if (!restricted || restrictedSince !== null) return;
5782
+ setRestrictedSince(Date.now());
5783
+ const timer = setTimeout(() => setTick((n) => n + 1), RESTRICTED_NOTICE_MS + 50);
5784
+ return () => clearTimeout(timer);
5785
+ }, [restricted, restrictedSince]);
5786
+ if (network === void 0 || snapshot?.phase !== "in_call" || connectionBanner) return null;
5787
+ const close = (code) => () => setClosed((prev) => /* @__PURE__ */ new Set([...prev, code]));
5788
+ const cameraOn = room.local?.cameraEnabled === true;
5789
+ if (network.videoPaused && !cameraOn) {
5790
+ return /* @__PURE__ */ jsxs4("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "video-paused", children: [
5791
+ "Your video is paused to save your audio",
5792
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: () => void room.toggleCamera(), children: "Try video again" })
5793
+ ] });
5794
+ }
5795
+ if (network.quality === "poor" && !closed.has("connection-poor")) {
5796
+ return /* @__PURE__ */ jsxs4("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "connection-poor", children: [
5797
+ "Your connection is unstable",
5798
+ cameraOn ? /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: () => void room.toggleCamera(), children: "Turn off camera" }) : null,
5799
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: close("connection-poor"), children: "Dismiss" })
5800
+ ] });
5801
+ }
5802
+ if (restricted && !closed.has("restricted-network") && restrictedSince !== null && Date.now() - restrictedSince < RESTRICTED_NOTICE_MS) {
5803
+ return /* @__PURE__ */ jsxs4("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "restricted-network", children: [
5804
+ "Restricted network \xB7 quality may be reduced",
5805
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: close("restricted-network"), children: "Dismiss" })
5806
+ ] });
5807
+ }
5808
+ return null;
5809
+ }
5710
5810
  function ShareNotice() {
5711
- const { share, actions } = useMeetCore();
5811
+ const { share, actions, snapshot } = useMeetCore();
5712
5812
  const [soundHintClosed, setSoundHintClosed] = useState5(null);
5713
5813
  if (share.state === "presenting" && share.audio === false && soundHintClosed !== share.at) {
5714
5814
  return /* @__PURE__ */ jsxs4("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-audio-missing", children: [
@@ -5717,6 +5817,16 @@ function ShareNotice() {
5717
5817
  /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: () => setSoundHintClosed(share.at), children: "Dismiss" })
5718
5818
  ] });
5719
5819
  }
5820
+ if (share.state === "ended") {
5821
+ const replaced = share.reason === "replaced";
5822
+ const rule = snapshot?.livePolicy.shareWhoCan ?? "host_only";
5823
+ const why = replaced ? `${share.by ?? "Someone"} is now presenting` : rule === "presenters" ? "Sharing is turned off. Only presenters can share their screen" : rule === "org" ? "Sharing is turned off. Only people in the organization can share their screen" : "Sharing is turned off. Only the host can share their screen";
5824
+ return /* @__PURE__ */ jsxs4("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": replaced ? "share-ended" : "share-unavailable", children: [
5825
+ replaced ? "Your presentation ended" : "Your presentation was stopped",
5826
+ /* @__PURE__ */ jsx6("span", { className: "mx-meet__remedy", children: why }),
5827
+ /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: () => actions.dismissShareNotice(), children: "Dismiss" })
5828
+ ] });
5829
+ }
5720
5830
  if (share.state !== "refused") return null;
5721
5831
  const text = 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";
5722
5832
  return /* @__PURE__ */ jsxs4("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-unavailable", children: [
@@ -5900,8 +6010,8 @@ function ExitScreen({ onLeave }) {
5900
6010
  ] });
5901
6011
  break;
5902
6012
  case "unsupported":
5903
- title = "This browser can't join meetings";
5904
- line = "Open the link in Chrome, Edge, Firefox or Safari.";
6013
+ title = "Meet is not compatible with your browser yet";
6014
+ line = "Open this link in Chrome, Edge, Firefox or Safari.";
5905
6015
  break;
5906
6016
  case "ended":
5907
6017
  case "record":
@@ -5928,6 +6038,9 @@ function ExitScreen({ onLeave }) {
5928
6038
  function WaitingRoom({ host, onLeave }) {
5929
6039
  const { waiting, withdraw, askAgain } = useWaiting();
5930
6040
  const { phaseNotice } = useMeetCore();
6041
+ const localMedia = useLocalMedia();
6042
+ const { region: mediaRegion } = useMediaSupervisor();
6043
+ const noMicrophone = localMedia.microphone.unavailable !== null || mediaRegion.preview.microphone.state === "unavailable";
5931
6044
  const [busy, setBusy] = useState5(false);
5932
6045
  const admission = waiting?.admission ?? "knocking";
5933
6046
  const reknockLeft = useCountdown(admission === "denied" ? waiting?.reknockAt ?? null : null);
@@ -5992,6 +6105,7 @@ function WaitingRoom({ host, onLeave }) {
5992
6105
  children: line
5993
6106
  }
5994
6107
  ) : null,
6108
+ noMicrophone && (admission === "knocking" || admission === "waiting_host") ? /* @__PURE__ */ jsx6("p", { className: "mx-meet__empty-body", role: "status", "data-meet-notice": "joining-without-microphone", children: "You're joining without a microphone. You'll be able to listen, but others won't hear you." }) : null,
5995
6109
  /* @__PURE__ */ jsx6("div", { className: "mx-meet__waiting-actions", children: actions })
5996
6110
  ] });
5997
6111
  }