@ai-matrx/meet 0.11.47 → 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;
@@ -1189,6 +1212,13 @@ function requireSession(session, action) {
1189
1212
  if (session === null) throw noRuntime(action);
1190
1213
  return session;
1191
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
+ }
1192
1222
  function useRoom() {
1193
1223
  const host = useMeetHost();
1194
1224
  const snapshot = useMeetSnapshot();
@@ -1203,7 +1233,9 @@ function useRoom() {
1203
1233
  secondDevice: snapshot?.secondDevice ?? null,
1204
1234
  local,
1205
1235
  // An observer (role `viewer`) publishes nothing: no Share screen to press and be refused (MD-16).
1206
- 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),
1207
1239
  join: useCallback2(
1208
1240
  async (request) => requireEngine(engine, "join").join(request),
1209
1241
  [engine]
@@ -1301,7 +1333,7 @@ function useLobby() {
1301
1333
  declined: (snapshot?.lobby ?? []).filter((entry) => entry.state === "denied"),
1302
1334
  // No runtime means no host powers — `<LobbyPanel>` renders nothing rather
1303
1335
  // than an admit button that would throw.
1304
- 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)),
1305
1337
  admit: useCallback2(
1306
1338
  async (entry) => requireSession(session, "admit").admit(entry, true),
1307
1339
  [session]
@@ -3415,7 +3447,8 @@ function ParticipantTile({
3415
3447
  participant.isLocal ? " (you)" : "",
3416
3448
  participant.isAgent ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__badge", children: "AI" }) : null,
3417
3449
  /* @__PURE__ */ jsx6(RoleBadge, { role: participant.role, standing: participant.standing }),
3418
- 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
3419
3452
  ] }),
3420
3453
  !participant.micEnabled ? /* @__PURE__ */ jsx6(MicOffIcon, { width: 14, height: 14 }) : null,
3421
3454
  /* @__PURE__ */ jsx6(QualityBars, { quality: participant.quality })
@@ -3505,6 +3538,8 @@ function ControlBar({ onLeave }) {
3505
3538
  const queue = useHandQueue();
3506
3539
  const micUnavailable = useLocalMedia().microphone.unavailable;
3507
3540
  const live = useLivePolicy();
3541
+ const core = useMeetCore();
3542
+ const [askPresentInstead, setAskPresentInstead] = useState5(false);
3508
3543
  const unread = useUnreadChat(panel === "chat");
3509
3544
  const [whiteboard, setWhiteboard] = useState5(false);
3510
3545
  const app = useMeetAppPanel();
@@ -3593,15 +3628,39 @@ function ControlBar({ onLeave }) {
3593
3628
  }
3594
3629
  ),
3595
3630
  /* @__PURE__ */ jsx6(CaptionsControl, {}),
3596
- room.canShareScreen || room.local?.screenSharing === true ? /* @__PURE__ */ jsx6(
3597
- ControlButton,
3598
- {
3599
- label: room.local?.screenSharing === true ? "Stop sharing" : "Share screen",
3600
- active: room.local?.screenSharing === true,
3601
- onClick: () => void room.toggleScreenShare(),
3602
- children: /* @__PURE__ */ jsx6(ScreenShareIcon, {})
3603
- }
3604
- ) : 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,
3605
3664
  /* @__PURE__ */ jsxs4("span", { className: "mx-meet__anchor", children: [
3606
3665
  /* @__PURE__ */ jsx6(
3607
3666
  ControlButton,
@@ -3698,7 +3757,7 @@ function ControlBar({ onLeave }) {
3698
3757
  ]
3699
3758
  }
3700
3759
  ),
3701
- !room.canShareScreen && room.local?.screenSharing !== true ? (
3760
+ !room.canShareScreen && room.sharingAllowed && room.local?.screenSharing !== true ? (
3702
3761
  // `share-unsupported-mobile`: the control is absent; the menu says where it lives.
3703
3762
  /* @__PURE__ */ jsxs4("p", { className: "mx-meet__menu-note", role: "note", "data-meet-notice": "share-unsupported", children: [
3704
3763
  /* @__PURE__ */ jsx6(ScreenShareIcon, { width: 16, height: 16 }),
@@ -5703,13 +5762,53 @@ function MeetSkinRoom({
5703
5762
  ] }),
5704
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,
5705
5764
  /* @__PURE__ */ jsx6(ShareNotice, {}),
5765
+ /* @__PURE__ */ jsx6(NetworkNotice, {}),
5706
5766
  /* @__PURE__ */ jsx6(Stage, {}),
5707
5767
  /* @__PURE__ */ jsx6(Captions, {}),
5708
5768
  /* @__PURE__ */ jsx6(ControlBar, { ...onLeave !== void 0 ? { onLeave } : {} })
5709
5769
  ] });
5710
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
+ }
5711
5810
  function ShareNotice() {
5712
- const { share, actions } = useMeetCore();
5811
+ const { share, actions, snapshot } = useMeetCore();
5713
5812
  const [soundHintClosed, setSoundHintClosed] = useState5(null);
5714
5813
  if (share.state === "presenting" && share.audio === false && soundHintClosed !== share.at) {
5715
5814
  return /* @__PURE__ */ jsxs4("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-audio-missing", children: [
@@ -5718,6 +5817,16 @@ function ShareNotice() {
5718
5817
  /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: () => setSoundHintClosed(share.at), children: "Dismiss" })
5719
5818
  ] });
5720
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
+ }
5721
5830
  if (share.state !== "refused") return null;
5722
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";
5723
5832
  return /* @__PURE__ */ jsxs4("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "share-unavailable", children: [