@ai-matrx/meet 0.11.42 → 0.11.43
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/CHANGELOG.md +6 -0
- package/dist/{collab-Bf5evcLO.d.cts → collab-BNJNgXFB.d.cts} +10 -1
- package/dist/{collab-Bf5evcLO.d.ts → collab-BNJNgXFB.d.ts} +10 -1
- package/dist/{components-Frpvls4N.d.ts → components-BDtGzHf4.d.ts} +4 -2
- package/dist/{components-roCEgyFH.d.cts → components-BxK993iW.d.cts} +4 -2
- package/dist/core.cjs +182 -14
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +56 -6
- package/dist/core.d.ts +56 -6
- package/dist/core.js +182 -14
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +182 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +182 -14
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +456 -54
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +21 -4
- package/dist/react.d.ts +21 -4
- package/dist/react.js +456 -54
- package/dist/react.js.map +1 -1
- package/dist/skins/meet.cjs +321 -47
- package/dist/skins/meet.cjs.map +1 -1
- package/dist/skins/meet.d.cts +5 -3
- package/dist/skins/meet.d.ts +5 -3
- package/dist/skins/meet.js +321 -47
- package/dist/skins/meet.js.map +1 -1
- package/package.json +3 -3
package/dist/react.cjs
CHANGED
|
@@ -122,6 +122,7 @@ __export(react_exports, {
|
|
|
122
122
|
autoAssign: () => autoAssign,
|
|
123
123
|
autoRejoinDelayMs: () => autoRejoinDelayMs,
|
|
124
124
|
availableActivities: () => availableActivities,
|
|
125
|
+
breakoutClosingMs: () => breakoutClosingMs,
|
|
125
126
|
breakoutRemainingMs: () => breakoutRemainingMs,
|
|
126
127
|
callInviteAction: () => callInviteAction,
|
|
127
128
|
callInviteText: () => callInviteText,
|
|
@@ -289,6 +290,7 @@ __export(react_exports, {
|
|
|
289
290
|
useCanModerate: () => useCanModerate,
|
|
290
291
|
useCanRecord: () => useCanRecord,
|
|
291
292
|
useCaptionTranslation: () => useCaptionTranslation,
|
|
293
|
+
useCaptions: () => useCaptions,
|
|
292
294
|
useCollab: () => useCollab,
|
|
293
295
|
useCollabPanelRequest: () => useCollabPanelRequest,
|
|
294
296
|
useConsentNotice: () => useConsentNotice,
|
|
@@ -1107,6 +1109,8 @@ var MEET_ROUTES = {
|
|
|
1107
1109
|
noteTakerStop: "/api/v1/meet/agent/stop",
|
|
1108
1110
|
/** In-meeting Q&A. Streams NDJSON; body is `{meeting_id, question}` only. */
|
|
1109
1111
|
intelligenceAsk: "/api/v1/meet/intelligence/ask",
|
|
1112
|
+
/** Anyone in the meeting (guests with the room pass included) asks for captions for themselves. */
|
|
1113
|
+
noteTakerCaptions: "/api/v1/meet/agent/captions",
|
|
1110
1114
|
/** A host ends the meeting for everyone. Served by MRI-A6. */
|
|
1111
1115
|
meetingEnd: "/api/v1/meet/end",
|
|
1112
1116
|
/**
|
|
@@ -1253,7 +1257,8 @@ function parseRoomToken(body, roomName) {
|
|
|
1253
1257
|
breakoutRoomId: readString(payload, "breakout_room_id") ?? null,
|
|
1254
1258
|
observersVisibleTo: readString(payload, "observers_visible_to") === "hosts" ? "hosts" : "everyone",
|
|
1255
1259
|
connectionPolicy: connectionPolicyFrom(payload["connection_policy"]),
|
|
1256
|
-
leavePolicy: leavePolicyFrom(payload["leave_policy"])
|
|
1260
|
+
leavePolicy: leavePolicyFrom(payload["leave_policy"]),
|
|
1261
|
+
canReclaim: payload["can_reclaim"] === true
|
|
1257
1262
|
};
|
|
1258
1263
|
}
|
|
1259
1264
|
function createTokenClient(options) {
|
|
@@ -1715,6 +1720,42 @@ function createMeetAi(options) {
|
|
|
1715
1720
|
},
|
|
1716
1721
|
"meet.ai.startNoteTaker"
|
|
1717
1722
|
),
|
|
1723
|
+
requestCaptions: async ({ meetingId }) => {
|
|
1724
|
+
const roomToken = options.resolveRoomToken?.() ?? null;
|
|
1725
|
+
const operation = "meet.ai.requestCaptions";
|
|
1726
|
+
try {
|
|
1727
|
+
const response = await send({
|
|
1728
|
+
path: MEET_ROUTES.noteTakerCaptions,
|
|
1729
|
+
body: { meeting_id: meetingId },
|
|
1730
|
+
operation,
|
|
1731
|
+
requireAuth: roomToken === null,
|
|
1732
|
+
...roomToken !== null ? { headers: { [MEETING_PASS_HEADER]: roomToken } } : {}
|
|
1733
|
+
});
|
|
1734
|
+
if (!response.ok) {
|
|
1735
|
+
options.store.patch({ captionsRefusal: await refusalFor(response, operation) });
|
|
1736
|
+
return;
|
|
1737
|
+
}
|
|
1738
|
+
let payload = null;
|
|
1739
|
+
try {
|
|
1740
|
+
payload = await response.json();
|
|
1741
|
+
} catch {
|
|
1742
|
+
payload = null;
|
|
1743
|
+
}
|
|
1744
|
+
const row = typeof payload === "object" && payload !== null ? payload : {};
|
|
1745
|
+
const detail = typeof row["detail"] === "string" ? row["detail"] : "";
|
|
1746
|
+
options.store.patch({
|
|
1747
|
+
captionsRefusal: row["dispatched"] === true ? null : { status: 200, reason: detail, remedy: "" }
|
|
1748
|
+
});
|
|
1749
|
+
} catch (error) {
|
|
1750
|
+
options.store.patch({
|
|
1751
|
+
captionsRefusal: {
|
|
1752
|
+
status: 0,
|
|
1753
|
+
reason: error.message,
|
|
1754
|
+
remedy: error.remedy ?? ""
|
|
1755
|
+
}
|
|
1756
|
+
});
|
|
1757
|
+
}
|
|
1758
|
+
},
|
|
1718
1759
|
stopNoteTaker: ({ meetingId }) => dispatch(
|
|
1719
1760
|
MEET_ROUTES.noteTakerStop,
|
|
1720
1761
|
{ meeting_id: meetingId },
|
|
@@ -2701,6 +2742,55 @@ function recordingLabel(state) {
|
|
|
2701
2742
|
}
|
|
2702
2743
|
}
|
|
2703
2744
|
|
|
2745
|
+
// src/core/live-policy.ts
|
|
2746
|
+
var DEFAULT_LIVE_POLICY = {
|
|
2747
|
+
chatMode: "everyone",
|
|
2748
|
+
avLock: "off",
|
|
2749
|
+
timeLimitMinutes: 0,
|
|
2750
|
+
timeLimitWarningMinutes: 5,
|
|
2751
|
+
recordingConsent: "notice"
|
|
2752
|
+
};
|
|
2753
|
+
function oneOf(value, allowed, fallback) {
|
|
2754
|
+
return typeof value === "string" && allowed.includes(value) ? value : fallback;
|
|
2755
|
+
}
|
|
2756
|
+
function whole(value, fallback) {
|
|
2757
|
+
const n = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
|
|
2758
|
+
return Number.isFinite(n) && n >= 0 ? Math.floor(n) : fallback;
|
|
2759
|
+
}
|
|
2760
|
+
function livePolicyFrom(raw) {
|
|
2761
|
+
const o = typeof raw === "object" && raw !== null ? raw : {};
|
|
2762
|
+
return {
|
|
2763
|
+
chatMode: oneOf(o["chat_mode"], ["everyone", "hosts_only", "off"], DEFAULT_LIVE_POLICY.chatMode),
|
|
2764
|
+
avLock: oneOf(o["attendee_av_lock"], ["off", "mic", "camera", "both"], DEFAULT_LIVE_POLICY.avLock),
|
|
2765
|
+
timeLimitMinutes: whole(o["time_limit_minutes"], 0),
|
|
2766
|
+
timeLimitWarningMinutes: whole(o["time_limit_warning_minutes"], DEFAULT_LIVE_POLICY.timeLimitWarningMinutes),
|
|
2767
|
+
recordingConsent: oneOf(
|
|
2768
|
+
o["recording_consent"],
|
|
2769
|
+
["notice", "ok_or_leave", "view_only_until_agree"],
|
|
2770
|
+
DEFAULT_LIVE_POLICY.recordingConsent
|
|
2771
|
+
)
|
|
2772
|
+
};
|
|
2773
|
+
}
|
|
2774
|
+
function samePolicy(a, b) {
|
|
2775
|
+
return a.chatMode === b.chatMode && a.avLock === b.avLock && a.timeLimitMinutes === b.timeLimitMinutes && a.timeLimitWarningMinutes === b.timeLimitWarningMinutes && a.recordingConsent === b.recordingConsent;
|
|
2776
|
+
}
|
|
2777
|
+
var MODERATORS = /* @__PURE__ */ new Set(["host", "cohost"]);
|
|
2778
|
+
function isLockedFor(policy, role, kind) {
|
|
2779
|
+
if (MODERATORS.has(role)) return false;
|
|
2780
|
+
return policy.avLock === "both" || (kind === "microphone" ? policy.avLock === "mic" : policy.avLock === "camera");
|
|
2781
|
+
}
|
|
2782
|
+
function consentPending(policy, role, recordingOn, given) {
|
|
2783
|
+
return recordingOn && !given && policy.recordingConsent !== "notice" && !MODERATORS.has(role);
|
|
2784
|
+
}
|
|
2785
|
+
function canChat(policy, role) {
|
|
2786
|
+
if (policy.chatMode === "everyone") return true;
|
|
2787
|
+
return policy.chatMode === "hosts_only" && MODERATORS.has(role);
|
|
2788
|
+
}
|
|
2789
|
+
function minutesLeft(policy, startedAtMs, nowMs) {
|
|
2790
|
+
if (policy.timeLimitMinutes <= 0 || startedAtMs === null) return null;
|
|
2791
|
+
return Math.ceil((startedAtMs + policy.timeLimitMinutes * 6e4 - nowMs) / 6e4);
|
|
2792
|
+
}
|
|
2793
|
+
|
|
2704
2794
|
// src/core/attendance.ts
|
|
2705
2795
|
var IDLE_MEDIA_REGION = {
|
|
2706
2796
|
permission: { microphone: "unknown", camera: "unknown" },
|
|
@@ -3827,6 +3917,8 @@ function planFromRow(row) {
|
|
|
3827
3917
|
minutes: num2(row["minutes"]) ?? 0,
|
|
3828
3918
|
openedAt: str(row["opened_at"]),
|
|
3829
3919
|
endsAt: str(row["ends_at"]),
|
|
3920
|
+
closingAt: str(row["closing_at"]),
|
|
3921
|
+
moveMode: row["move_mode"] === "auto" ? "auto" : "invite",
|
|
3830
3922
|
closedAt: str(row["closed_at"]),
|
|
3831
3923
|
broadcasts: (Array.isArray(row["broadcasts"]) ? row["broadcasts"] : []).map(broadcastFrom).filter((b) => b !== null)
|
|
3832
3924
|
};
|
|
@@ -3978,9 +4070,17 @@ function autoAssign(identities, rooms, exclude = []) {
|
|
|
3978
4070
|
return out;
|
|
3979
4071
|
}
|
|
3980
4072
|
function breakoutRemainingMs(plan, now) {
|
|
3981
|
-
if (plan === null || plan.state !== "open"
|
|
3982
|
-
const
|
|
3983
|
-
|
|
4073
|
+
if (plan === null || plan.state !== "open") return null;
|
|
4074
|
+
const ends = [plan.endsAt, plan.closingAt].map((stamp) => stamp === null ? Number.NaN : Date.parse(stamp)).filter((time) => !Number.isNaN(time));
|
|
4075
|
+
if (ends.length === 0) return null;
|
|
4076
|
+
return Math.max(0, Math.min(...ends) - now);
|
|
4077
|
+
}
|
|
4078
|
+
var BREAKOUT_COUNTDOWN_MS = 3e4;
|
|
4079
|
+
function breakoutClosingMs(plan, now) {
|
|
4080
|
+
if (plan === null || plan.state !== "open") return null;
|
|
4081
|
+
const remaining = breakoutRemainingMs(plan, now);
|
|
4082
|
+
if (remaining === null) return null;
|
|
4083
|
+
return plan.closingAt !== null || remaining <= BREAKOUT_COUNTDOWN_MS ? remaining : null;
|
|
3984
4084
|
}
|
|
3985
4085
|
function defaultCaptionLanguage(settings, viewerLanguage) {
|
|
3986
4086
|
const configured = settings?.captionTranslationLanguage.trim() ?? "";
|
|
@@ -4157,6 +4257,11 @@ var EMPTY = {
|
|
|
4157
4257
|
presenter: null,
|
|
4158
4258
|
waitingCount: 0,
|
|
4159
4259
|
leavePolicy: DEFAULT_LEAVE_POLICY,
|
|
4260
|
+
livePolicy: DEFAULT_LIVE_POLICY,
|
|
4261
|
+
canReclaim: false,
|
|
4262
|
+
recordingConsentGiven: false,
|
|
4263
|
+
sessionStartedAt: null,
|
|
4264
|
+
mutedByHost: false,
|
|
4160
4265
|
moderationNotice: null,
|
|
4161
4266
|
actionNotice: null,
|
|
4162
4267
|
privateChatRequest: null,
|
|
@@ -4166,6 +4271,8 @@ var EMPTY = {
|
|
|
4166
4271
|
names: {},
|
|
4167
4272
|
wrapUp: null,
|
|
4168
4273
|
noteTakerRefusal: null,
|
|
4274
|
+
captionsOn: false,
|
|
4275
|
+
captionsRefusal: null,
|
|
4169
4276
|
media: IDLE_MEDIA,
|
|
4170
4277
|
mediaRegion: IDLE_MEDIA_REGION,
|
|
4171
4278
|
failure: null,
|
|
@@ -4370,7 +4477,12 @@ function createMeetStore(initial = {}) {
|
|
|
4370
4477
|
if (current.enabled === next.enabled && current.unavailable?.code === next.unavailable?.code && current.unavailable?.reason === next.unavailable?.reason && current.unavailable?.message === next.unavailable?.message) {
|
|
4371
4478
|
return;
|
|
4372
4479
|
}
|
|
4373
|
-
commit({
|
|
4480
|
+
commit({
|
|
4481
|
+
...state,
|
|
4482
|
+
media: { ...state.media, [kind]: next },
|
|
4483
|
+
// The person turned their own microphone on again: the host's mute is over.
|
|
4484
|
+
...kind === "microphone" && next.enabled && state.mutedByHost ? { mutedByHost: false } : {}
|
|
4485
|
+
});
|
|
4374
4486
|
},
|
|
4375
4487
|
reset() {
|
|
4376
4488
|
const phase = nextPhase(state.phase, { type: "reset", teardown: true }) ?? EMPTY.phase;
|
|
@@ -4432,6 +4544,7 @@ function applyDoorRow(store, attendee) {
|
|
|
4432
4544
|
function decidedState(state) {
|
|
4433
4545
|
return WAITING_ADMISSION_STATES.includes(state) ? "expired" : state;
|
|
4434
4546
|
}
|
|
4547
|
+
var LIVE_POLICY_POLL_MS = 3e3;
|
|
4435
4548
|
function createMeetingSession(options) {
|
|
4436
4549
|
const { manager, repository, store, identity } = options;
|
|
4437
4550
|
let channel = null;
|
|
@@ -4573,6 +4686,46 @@ function createMeetingSession(options) {
|
|
|
4573
4686
|
stopWaiting();
|
|
4574
4687
|
}
|
|
4575
4688
|
}
|
|
4689
|
+
let policyTimer = null;
|
|
4690
|
+
let policyReading = false;
|
|
4691
|
+
async function readLivePolicy() {
|
|
4692
|
+
const meeting = store.snapshot().meeting;
|
|
4693
|
+
const pass = options.resolvePass?.() ?? null;
|
|
4694
|
+
if (meeting === null || pass === null || policyReading) return;
|
|
4695
|
+
policyReading = true;
|
|
4696
|
+
try {
|
|
4697
|
+
const response = await options.api.request({
|
|
4698
|
+
path: `${MEET_ROUTES.state}?meeting_id=${encodeURIComponent(meeting.id)}`,
|
|
4699
|
+
method: "GET",
|
|
4700
|
+
requireAuth: false,
|
|
4701
|
+
operation: "meet.state(policy)",
|
|
4702
|
+
headers: { [MEETING_PASS_HEADER]: pass }
|
|
4703
|
+
});
|
|
4704
|
+
if (!response.ok) return;
|
|
4705
|
+
const read = await response.json();
|
|
4706
|
+
const next = livePolicyFrom(read.live_policy);
|
|
4707
|
+
const started = read.session_started_at ? Date.parse(read.session_started_at) : NaN;
|
|
4708
|
+
const snapshot = store.snapshot();
|
|
4709
|
+
const startedAt = Number.isFinite(started) ? started : snapshot.sessionStartedAt;
|
|
4710
|
+
if (!samePolicy(snapshot.livePolicy, next) || startedAt !== snapshot.sessionStartedAt) {
|
|
4711
|
+
store.patch({ livePolicy: next, sessionStartedAt: startedAt });
|
|
4712
|
+
}
|
|
4713
|
+
} catch {
|
|
4714
|
+
} finally {
|
|
4715
|
+
policyReading = false;
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
function syncLivePolicyTimer() {
|
|
4719
|
+
const inCall = store.snapshot().phase === "in_call";
|
|
4720
|
+
if (inCall && policyTimer === null) {
|
|
4721
|
+
void readLivePolicy();
|
|
4722
|
+
policyTimer = setInterval(() => void readLivePolicy(), LIVE_POLICY_POLL_MS);
|
|
4723
|
+
} else if (!inCall && policyTimer !== null) {
|
|
4724
|
+
clearInterval(policyTimer);
|
|
4725
|
+
policyTimer = null;
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
4728
|
+
const unsubscribePolicy = store.subscribe(syncLivePolicyTimer);
|
|
4576
4729
|
async function readState2() {
|
|
4577
4730
|
if (reading !== null) return reading;
|
|
4578
4731
|
const meeting = store.snapshot().meeting;
|
|
@@ -4959,6 +5112,9 @@ function createMeetingSession(options) {
|
|
|
4959
5112
|
},
|
|
4960
5113
|
dispose() {
|
|
4961
5114
|
stopWaiting();
|
|
5115
|
+
unsubscribePolicy();
|
|
5116
|
+
if (policyTimer !== null) clearInterval(policyTimer);
|
|
5117
|
+
policyTimer = null;
|
|
4962
5118
|
channel?.close();
|
|
4963
5119
|
channel = null;
|
|
4964
5120
|
meetingChannel?.close();
|
|
@@ -5548,10 +5704,11 @@ function moderationText(kind, payload) {
|
|
|
5548
5704
|
switch (kind) {
|
|
5549
5705
|
case "muted": {
|
|
5550
5706
|
const source = text2(payload["source"]) ?? "microphone";
|
|
5551
|
-
|
|
5707
|
+
const who = payload["by_role"] === "host" || payload["by_role"] === "cohost" ? "the host" : by;
|
|
5708
|
+
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}.`;
|
|
5552
5709
|
}
|
|
5553
5710
|
case "ask-unmute":
|
|
5554
|
-
return `${by}
|
|
5711
|
+
return `${by === "The host" ? "The host" : by} asked you to unmute.`;
|
|
5555
5712
|
case "removed":
|
|
5556
5713
|
return `${by} removed you from this meeting.`;
|
|
5557
5714
|
default:
|
|
@@ -5577,7 +5734,7 @@ function applyModeration(store, payload, now) {
|
|
|
5577
5734
|
grants: { ...snapshot.grants, role },
|
|
5578
5735
|
moderationNotice: {
|
|
5579
5736
|
kind: "role",
|
|
5580
|
-
text: role === "host" ? "You are now the host." : role === "cohost" ? "You
|
|
5737
|
+
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.",
|
|
5581
5738
|
at: now()
|
|
5582
5739
|
}
|
|
5583
5740
|
});
|
|
@@ -5589,7 +5746,10 @@ function applyModeration(store, payload, now) {
|
|
|
5589
5746
|
case "removed": {
|
|
5590
5747
|
const sentence = moderationText(kind, payload);
|
|
5591
5748
|
if (sentence === null) return ignored("a moderation event with no sentence");
|
|
5592
|
-
store.patch({
|
|
5749
|
+
store.patch({
|
|
5750
|
+
moderationNotice: { kind, text: sentence, at: now() },
|
|
5751
|
+
...kind === "muted" && (text2(payload["source"]) ?? "microphone") === "microphone" ? { mutedByHost: true } : {}
|
|
5752
|
+
});
|
|
5593
5753
|
return HANDLED;
|
|
5594
5754
|
}
|
|
5595
5755
|
default:
|
|
@@ -5628,7 +5788,7 @@ function exactNoteTakerParticipant(meeting, participants) {
|
|
|
5628
5788
|
}
|
|
5629
5789
|
function noteTakerStatus(args) {
|
|
5630
5790
|
const present = exactNoteTakerParticipant(args.meeting, args.participants) !== null;
|
|
5631
|
-
const raw = args.meeting?.metadata[NOTE_TAKER_METADATA_KEY];
|
|
5791
|
+
const raw = args.meeting?.metadata?.[NOTE_TAKER_METADATA_KEY];
|
|
5632
5792
|
const row = typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : {};
|
|
5633
5793
|
const recorded = readState(row["state"]);
|
|
5634
5794
|
const detail = typeof row["reason"] === "string" && row["reason"].length > 0 ? row["reason"] : null;
|
|
@@ -6105,6 +6265,7 @@ function createRoomEngine(options) {
|
|
|
6105
6265
|
lastServerUrl = token.serverUrl;
|
|
6106
6266
|
connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;
|
|
6107
6267
|
if (token.leavePolicy !== void 0) store.patch({ leavePolicy: token.leavePolicy });
|
|
6268
|
+
store.patch({ canReclaim: token.canReclaim === true });
|
|
6108
6269
|
store.patch({
|
|
6109
6270
|
localIdentity: token.identity,
|
|
6110
6271
|
breakoutRoomId: token.breakoutRoomId ?? null,
|
|
@@ -6604,8 +6765,12 @@ var MODERATION_ACTIONS = [
|
|
|
6604
6765
|
"lower_hand",
|
|
6605
6766
|
"lower_all_hands",
|
|
6606
6767
|
"make_cohost",
|
|
6768
|
+
"make_presenter",
|
|
6769
|
+
"revoke_presenter",
|
|
6770
|
+
"set_av_lock",
|
|
6607
6771
|
"revoke_cohost",
|
|
6608
6772
|
"transfer_host",
|
|
6773
|
+
"reclaim_host",
|
|
6609
6774
|
"rename",
|
|
6610
6775
|
"remove",
|
|
6611
6776
|
"report"
|
|
@@ -7643,11 +7808,13 @@ var import_react3 = require("react");
|
|
|
7643
7808
|
|
|
7644
7809
|
// src/core/observation.ts
|
|
7645
7810
|
function observedRole(role, standing) {
|
|
7646
|
-
if (role === "host" || role === "cohost") return role;
|
|
7811
|
+
if (role === "host" || role === "cohost" || role === "presenter") return role;
|
|
7647
7812
|
return isLinkFollower(standing) ? "guest" : "participant";
|
|
7648
7813
|
}
|
|
7649
|
-
function observedDevice(track, permission = "unknown") {
|
|
7814
|
+
function observedDevice(track, permission = "unknown", held = null) {
|
|
7815
|
+
if (held === "locked") return "locked";
|
|
7650
7816
|
if (track.enabled) return "on";
|
|
7817
|
+
if (held === "muted_by_host") return "muted_by_host";
|
|
7651
7818
|
const reason = deviceUnavailableReason(track, permission);
|
|
7652
7819
|
return reason === null ? "muted_by_me" : `unavailable:${reason}`;
|
|
7653
7820
|
}
|
|
@@ -7684,10 +7851,20 @@ function observe(input) {
|
|
|
7684
7851
|
recording: snapshot?.recording === "recording" || snapshot?.recording === "stopping",
|
|
7685
7852
|
// S6c: the autoplay region is known only inside the room (no remote audio exists elsewhere).
|
|
7686
7853
|
audioBlocked: inCall && snapshot !== null ? snapshot.mediaRegion.audioOutput === "blocked" : null,
|
|
7687
|
-
camera: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.camera, snapshot.mediaRegion.permission.camera) : null,
|
|
7688
|
-
microphone: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.microphone, snapshot.mediaRegion.permission.microphone) : null
|
|
7854
|
+
camera: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.camera, snapshot.mediaRegion.permission.camera, lockedFor(snapshot, "camera", inCall)) : null,
|
|
7855
|
+
microphone: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.microphone, snapshot.mediaRegion.permission.microphone, lockedFor(snapshot, "microphone", inCall)) : null
|
|
7689
7856
|
};
|
|
7690
7857
|
}
|
|
7858
|
+
function lockedFor(snapshot, kind, inCall) {
|
|
7859
|
+
if (!inCall) return null;
|
|
7860
|
+
const role = effectiveRole(snapshot);
|
|
7861
|
+
if (isLockedFor(snapshot.livePolicy, role, kind)) return "locked";
|
|
7862
|
+
if (snapshot.livePolicy.recordingConsent === "view_only_until_agree" && consentPending(snapshot.livePolicy, role, snapshot.recording === "recording", snapshot.recordingConsentGiven)) {
|
|
7863
|
+
return "locked";
|
|
7864
|
+
}
|
|
7865
|
+
if (kind === "microphone" && snapshot.mutedByHost) return "muted_by_host";
|
|
7866
|
+
return null;
|
|
7867
|
+
}
|
|
7691
7868
|
function staticObservation(phase, reason = null) {
|
|
7692
7869
|
return {
|
|
7693
7870
|
phase,
|
|
@@ -8877,6 +9054,45 @@ function useMeetAi() {
|
|
|
8877
9054
|
}, [ai, meeting, dispatchNoteTaker])
|
|
8878
9055
|
};
|
|
8879
9056
|
}
|
|
9057
|
+
function useCaptions() {
|
|
9058
|
+
const host = useMeetHost();
|
|
9059
|
+
const snapshot = useMeetSnapshot();
|
|
9060
|
+
const ai = useMeetAi();
|
|
9061
|
+
const on = snapshot?.captionsOn === true;
|
|
9062
|
+
const refusal2 = snapshot?.captionsRefusal ?? null;
|
|
9063
|
+
const taker = ai.noteTaker;
|
|
9064
|
+
const meeting = snapshot?.meeting ?? null;
|
|
9065
|
+
const request = (0, import_react4.useCallback)(() => {
|
|
9066
|
+
if (host === null || meeting === null) return;
|
|
9067
|
+
void host.ai.requestCaptions({ meetingId: meeting.id });
|
|
9068
|
+
}, [host, meeting]);
|
|
9069
|
+
const toggle = (0, import_react4.useCallback)(() => {
|
|
9070
|
+
if (host === null) return;
|
|
9071
|
+
const next = !on;
|
|
9072
|
+
host.store.patch({ captionsOn: next, captionsRefusal: null });
|
|
9073
|
+
if (next && taker.state !== "joining" && taker.state !== "transcribing") request();
|
|
9074
|
+
}, [host, on, taker.state, request]);
|
|
9075
|
+
let state = "off";
|
|
9076
|
+
let reason = null;
|
|
9077
|
+
let remedy = null;
|
|
9078
|
+
if (on) {
|
|
9079
|
+
if (taker.state === "transcribing" && taker.present) {
|
|
9080
|
+
state = "live";
|
|
9081
|
+
} else if (taker.state === "joining") {
|
|
9082
|
+
state = "starting";
|
|
9083
|
+
} else if (refusal2 !== null && refusal2.reason.length > 0) {
|
|
9084
|
+
state = "unavailable";
|
|
9085
|
+
reason = refusal2.reason;
|
|
9086
|
+
remedy = refusal2.remedy.length > 0 ? refusal2.remedy : null;
|
|
9087
|
+
} else if (taker.state === "ended" || taker.state === "handoff_pending") {
|
|
9088
|
+
state = "unavailable";
|
|
9089
|
+
reason = noteTakerLabel(taker);
|
|
9090
|
+
} else {
|
|
9091
|
+
state = "starting";
|
|
9092
|
+
}
|
|
9093
|
+
}
|
|
9094
|
+
return { on, state, reason, remedy, toggle };
|
|
9095
|
+
}
|
|
8880
9096
|
function useEndMeeting() {
|
|
8881
9097
|
const host = useMeetHost();
|
|
8882
9098
|
const snapshot = useMeetSnapshot();
|
|
@@ -8914,7 +9130,7 @@ function useConsentNotice() {
|
|
|
8914
9130
|
const snapshot = useMeetSnapshot();
|
|
8915
9131
|
const agent = noteTakerParticipant(snapshot?.participants ?? []);
|
|
8916
9132
|
if (agent === null) return null;
|
|
8917
|
-
return
|
|
9133
|
+
return `AI notes are on. ${agent.displayName} is in this meeting, transcribing what is said and writing notes everyone here can read.`;
|
|
8918
9134
|
}
|
|
8919
9135
|
function useMediaSupervisor() {
|
|
8920
9136
|
const host = useMeetHost();
|
|
@@ -9378,6 +9594,38 @@ function useGuestRecordAccess(meeting) {
|
|
|
9378
9594
|
)
|
|
9379
9595
|
};
|
|
9380
9596
|
}
|
|
9597
|
+
function useLivePolicy() {
|
|
9598
|
+
const snapshot = useMeetSnapshot();
|
|
9599
|
+
const role = useRole();
|
|
9600
|
+
const [now, setNow] = (0, import_react4.useState)(() => Date.now());
|
|
9601
|
+
const limited = (snapshot?.livePolicy.timeLimitMinutes ?? 0) > 0;
|
|
9602
|
+
(0, import_react4.useEffect)(() => {
|
|
9603
|
+
if (!limited) return void 0;
|
|
9604
|
+
const timer = setInterval(() => setNow(Date.now()), 1e3);
|
|
9605
|
+
return () => clearInterval(timer);
|
|
9606
|
+
}, [limited]);
|
|
9607
|
+
const policy = snapshot?.livePolicy ?? DEFAULT_LIVE_POLICY;
|
|
9608
|
+
const left = minutesLeft(policy, snapshot?.sessionStartedAt ?? null, now);
|
|
9609
|
+
const host = useMeetHost();
|
|
9610
|
+
const pending = consentPending(policy, role, snapshot?.recording === "recording", snapshot?.recordingConsentGiven === true);
|
|
9611
|
+
const viewOnly = pending && policy.recordingConsent === "view_only_until_agree";
|
|
9612
|
+
const idle = snapshot !== null && snapshot.recording !== "recording" && snapshot.recording !== "starting" && snapshot.recording !== "stopping";
|
|
9613
|
+
const given = snapshot?.recordingConsentGiven === true;
|
|
9614
|
+
const store = host?.store ?? null;
|
|
9615
|
+
(0, import_react4.useEffect)(() => {
|
|
9616
|
+
if (idle && given) store?.patch({ recordingConsentGiven: false });
|
|
9617
|
+
}, [idle, given, store]);
|
|
9618
|
+
return {
|
|
9619
|
+
canChat: canChat(policy, role),
|
|
9620
|
+
chatMode: policy.chatMode,
|
|
9621
|
+
micLocked: isLockedFor(policy, role, "microphone") || viewOnly,
|
|
9622
|
+
cameraLocked: isLockedFor(policy, role, "camera") || viewOnly,
|
|
9623
|
+
consent: pending ? policy.recordingConsent === "ok_or_leave" ? "ok_or_leave" : "view_only_until_agree" : null,
|
|
9624
|
+
agree: () => store?.patch({ recordingConsentGiven: true }),
|
|
9625
|
+
minutesLeft: left,
|
|
9626
|
+
timeWarning: left !== null && left <= policy.timeLimitWarningMinutes
|
|
9627
|
+
};
|
|
9628
|
+
}
|
|
9381
9629
|
|
|
9382
9630
|
// src/react/skin-root.tsx
|
|
9383
9631
|
var import_react12 = require("react");
|
|
@@ -10152,6 +10400,18 @@ function SparklesIcon(props) {
|
|
|
10152
10400
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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" })
|
|
10153
10401
|
] });
|
|
10154
10402
|
}
|
|
10403
|
+
function CaptionsIcon(props) {
|
|
10404
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Icon, { ...props, children: [
|
|
10405
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "3", y: "5", width: "18", height: "14", rx: "2" }),
|
|
10406
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M10 10.5a2 2 0 1 0 0 3M16 10.5a2 2 0 1 0 0 3" })
|
|
10407
|
+
] });
|
|
10408
|
+
}
|
|
10409
|
+
function NotesIcon(props) {
|
|
10410
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Icon, { ...props, children: [
|
|
10411
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M4 20l1-4L16.5 4.5a2.1 2.1 0 0 1 3 3L8 19l-4 1Z" }),
|
|
10412
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M14 7l3 3" })
|
|
10413
|
+
] });
|
|
10414
|
+
}
|
|
10155
10415
|
function LockIcon(props) {
|
|
10156
10416
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Icon, { ...props, children: [
|
|
10157
10417
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "4", y: "10", width: "16", height: "11", rx: "2" }),
|
|
@@ -11058,7 +11318,7 @@ function BreakoutRun({ plan }) {
|
|
|
11058
11318
|
type: "button",
|
|
11059
11319
|
className: "mx-meet__primary mx-meet__primary--small",
|
|
11060
11320
|
onClick: () => void act({ action: "breakout_close" }).then(show),
|
|
11061
|
-
children: "
|
|
11321
|
+
children: "Close rooms"
|
|
11062
11322
|
}
|
|
11063
11323
|
) : null
|
|
11064
11324
|
] });
|
|
@@ -11076,9 +11336,14 @@ function BreakoutBanner() {
|
|
|
11076
11336
|
const { collab, act, moveTo, localIdentity, breakoutRoomId } = useCollab();
|
|
11077
11337
|
const plan = collab?.breakout ?? null;
|
|
11078
11338
|
const open = plan?.state === "open" ? plan : null;
|
|
11079
|
-
const now = useNow(open?.endsAt != null);
|
|
11339
|
+
const now = useNow(open?.endsAt != null || open?.closingAt != null);
|
|
11080
11340
|
const remaining = breakoutRemainingMs(open, now);
|
|
11341
|
+
const closing = breakoutClosingMs(open, now);
|
|
11081
11342
|
const assigned = assignedRoom(open, localIdentity);
|
|
11343
|
+
const [later, setLater] = (0, import_react9.useState)(null);
|
|
11344
|
+
const [autoSince, setAutoSince] = (0, import_react9.useState)(null);
|
|
11345
|
+
const AUTO_MOVE_MS = 1e4;
|
|
11346
|
+
const autoLeft = open?.moveMode === "auto" && assigned !== null && autoSince?.id === open.id && later !== open.id ? Math.max(0, AUTO_MOVE_MS - (now - autoSince.at)) : null;
|
|
11082
11347
|
const [dismissedBroadcast, setDismissedBroadcast] = (0, import_react9.useState)(null);
|
|
11083
11348
|
const [failure, setFailure] = (0, import_react9.useState)(null);
|
|
11084
11349
|
const expireAsked = (0, import_react9.useRef)(null);
|
|
@@ -11091,6 +11356,19 @@ function BreakoutBanner() {
|
|
|
11091
11356
|
if ((here ?? null) === request.roomId) return;
|
|
11092
11357
|
void moveTo(request.roomId).then((result) => setFailure(result.ok ? null : result.message));
|
|
11093
11358
|
}, [moveAt, host, moveTo]);
|
|
11359
|
+
const joinAssigned = async () => {
|
|
11360
|
+
if (assigned === null) return;
|
|
11361
|
+
const said = await act({ action: "breakout_join", roomId: assigned.id });
|
|
11362
|
+
const result = said.ok ? await moveTo(assigned.id) : said;
|
|
11363
|
+
setFailure(result.ok ? null : result.message);
|
|
11364
|
+
};
|
|
11365
|
+
(0, import_react9.useEffect)(() => {
|
|
11366
|
+
if (open === null || open.moveMode !== "auto" || assigned === null || breakoutRoomId !== null) return;
|
|
11367
|
+
if (autoSince?.id !== open.id) setAutoSince({ id: open.id, at: Date.now() });
|
|
11368
|
+
}, [open, assigned, breakoutRoomId, autoSince]);
|
|
11369
|
+
(0, import_react9.useEffect)(() => {
|
|
11370
|
+
if (autoLeft === 0) void joinAssigned();
|
|
11371
|
+
}, [autoLeft]);
|
|
11094
11372
|
(0, import_react9.useEffect)(() => {
|
|
11095
11373
|
if (open === null || remaining === null || remaining > 0) return;
|
|
11096
11374
|
const asked = expireAsked.current;
|
|
@@ -11104,27 +11382,25 @@ function BreakoutBanner() {
|
|
|
11104
11382
|
if (open === null && failure === null) return null;
|
|
11105
11383
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mx-meet__breakout-banner", role: "status", children: [
|
|
11106
11384
|
failure !== null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "mx-meet__collab-error", children: failure }) : null,
|
|
11385
|
+
closing !== null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { role: "status", "data-meet-notice": "breakout-closing", children: [
|
|
11386
|
+
"Rooms closing in ",
|
|
11387
|
+
Math.ceil(closing / 1e3),
|
|
11388
|
+
room !== null ? " \u2014 you return to the main meeting" : ""
|
|
11389
|
+
] }) : null,
|
|
11107
11390
|
room !== null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
|
|
11108
11391
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("strong", { children: room.name }),
|
|
11109
|
-
remaining !== null ? ` \xB7 ${clock(remaining)} left` : "",
|
|
11392
|
+
remaining !== null && closing === null ? ` \xB7 ${clock(remaining)} left` : "",
|
|
11110
11393
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "mx-meet__chip", onClick: () => void moveTo(null), children: "Return to main room" })
|
|
11394
|
+
] }) : assigned !== null && later !== open?.id ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
|
|
11395
|
+
autoLeft !== null ? `You're being moved to ${assigned.name} in ${Math.ceil(autoLeft / 1e3)} s` : `You're invited to ${assigned.name}`,
|
|
11396
|
+
remaining !== null && closing === null ? ` \xB7 ${clock(remaining)} left` : "",
|
|
11397
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "mx-meet__primary mx-meet__primary--small", onClick: () => void joinAssigned(), children: "Join room" }),
|
|
11398
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "mx-meet__chip", onClick: () => setLater(open?.id ?? null), children: "Later" })
|
|
11111
11399
|
] }) : assigned !== null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
|
|
11112
|
-
|
|
11113
|
-
|
|
11114
|
-
|
|
11115
|
-
|
|
11116
|
-
"button",
|
|
11117
|
-
{
|
|
11118
|
-
type: "button",
|
|
11119
|
-
className: "mx-meet__primary mx-meet__primary--small",
|
|
11120
|
-
onClick: () => void act({ action: "breakout_join", roomId: assigned.id }).then(async (said) => {
|
|
11121
|
-
const result = said.ok ? await moveTo(assigned.id) : said;
|
|
11122
|
-
setFailure(result.ok ? null : result.message);
|
|
11123
|
-
}),
|
|
11124
|
-
children: "Join"
|
|
11125
|
-
}
|
|
11126
|
-
)
|
|
11127
|
-
] }) : open?.allowChoose ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Breakout rooms are open \u2014 pick one under Activities." }) : null,
|
|
11400
|
+
assigned.name,
|
|
11401
|
+
" is open",
|
|
11402
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "mx-meet__chip", onClick: () => void joinAssigned(), children: "Join room" })
|
|
11403
|
+
] }) : open?.allowChoose ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Rooms are open \u2014 pick one under Activities." }) : null,
|
|
11128
11404
|
showBroadcast ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "mx-meet__breakout-broadcast", children: [
|
|
11129
11405
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("strong", { children: [
|
|
11130
11406
|
broadcast.by,
|
|
@@ -11172,6 +11448,33 @@ function CaptionLanguageSettings() {
|
|
|
11172
11448
|
collab?.captionTranslationError != null ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "mx-meet__panel-note mx-meet__collab-error", children: collab.captionTranslationError }) : null
|
|
11173
11449
|
] });
|
|
11174
11450
|
}
|
|
11451
|
+
function CaptionsControl() {
|
|
11452
|
+
const captions = useCaptions();
|
|
11453
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
11454
|
+
ControlButton,
|
|
11455
|
+
{
|
|
11456
|
+
label: captions.on ? "Turn off captions" : "Turn on captions",
|
|
11457
|
+
active: captions.on,
|
|
11458
|
+
pressed: captions.on,
|
|
11459
|
+
onClick: captions.toggle,
|
|
11460
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CaptionsIcon, {})
|
|
11461
|
+
}
|
|
11462
|
+
);
|
|
11463
|
+
}
|
|
11464
|
+
function TakeNotesControl() {
|
|
11465
|
+
const ai = useMeetAi();
|
|
11466
|
+
if (!ai.canControlNoteTaker) return null;
|
|
11467
|
+
const running = ai.noteTaker.state === "joining" || ai.noteTaker.state === "transcribing";
|
|
11468
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
11469
|
+
ControlButton,
|
|
11470
|
+
{
|
|
11471
|
+
label: running ? "Stop taking notes" : "Take notes for me",
|
|
11472
|
+
active: running,
|
|
11473
|
+
onClick: () => void (running ? ai.stopNoteTaker() : ai.startNoteTaker()),
|
|
11474
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(NotesIcon, {})
|
|
11475
|
+
}
|
|
11476
|
+
);
|
|
11477
|
+
}
|
|
11175
11478
|
|
|
11176
11479
|
// src/skins/meet/components.tsx
|
|
11177
11480
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
@@ -11222,6 +11525,7 @@ function ControlButton({
|
|
|
11222
11525
|
label,
|
|
11223
11526
|
active = false,
|
|
11224
11527
|
danger = false,
|
|
11528
|
+
pressed,
|
|
11225
11529
|
onClick,
|
|
11226
11530
|
children
|
|
11227
11531
|
}) {
|
|
@@ -11236,26 +11540,49 @@ function ControlButton({
|
|
|
11236
11540
|
].filter(Boolean).join(" "),
|
|
11237
11541
|
"aria-label": label,
|
|
11238
11542
|
title: label,
|
|
11543
|
+
...pressed !== void 0 ? { "aria-pressed": pressed } : {},
|
|
11239
11544
|
onClick,
|
|
11240
11545
|
children
|
|
11241
11546
|
}
|
|
11242
11547
|
);
|
|
11243
11548
|
}
|
|
11244
11549
|
function RecordingIndicator() {
|
|
11245
|
-
const { state, label } = useRecording();
|
|
11246
|
-
|
|
11247
|
-
|
|
11248
|
-
|
|
11249
|
-
|
|
11250
|
-
|
|
11251
|
-
|
|
11252
|
-
|
|
11253
|
-
|
|
11254
|
-
|
|
11255
|
-
|
|
11256
|
-
|
|
11257
|
-
|
|
11258
|
-
);
|
|
11550
|
+
const { state, label, canStart } = useRecording();
|
|
11551
|
+
const snapshot = useMeetSnapshot();
|
|
11552
|
+
const previous = (0, import_react10.useRef)(state);
|
|
11553
|
+
const [stoppedAt, setStoppedAt] = (0, import_react10.useState)(null);
|
|
11554
|
+
(0, import_react10.useEffect)(() => {
|
|
11555
|
+
const was = previous.current;
|
|
11556
|
+
previous.current = state;
|
|
11557
|
+
if ((was === "recording" || was === "starting") && state !== "recording" && state !== "starting") {
|
|
11558
|
+
setStoppedAt(Date.now());
|
|
11559
|
+
const timer = setTimeout(() => setStoppedAt(null), 1e4);
|
|
11560
|
+
return () => clearTimeout(timer);
|
|
11561
|
+
}
|
|
11562
|
+
return void 0;
|
|
11563
|
+
}, [state]);
|
|
11564
|
+
const meetingId = snapshot?.meeting?.id ?? null;
|
|
11565
|
+
if (label === null && stoppedAt === null) return null;
|
|
11566
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
11567
|
+
label !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
11568
|
+
"span",
|
|
11569
|
+
{
|
|
11570
|
+
className: `mx-meet__recording mx-meet__recording--${state}`,
|
|
11571
|
+
role: "status",
|
|
11572
|
+
"aria-live": "polite",
|
|
11573
|
+
children: [
|
|
11574
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RecordIcon, { width: 10, height: 10 }),
|
|
11575
|
+
label
|
|
11576
|
+
]
|
|
11577
|
+
}
|
|
11578
|
+
) : null,
|
|
11579
|
+
stoppedAt !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "mx-meet__recording", role: "status", "data-meet-notice": "recording-stopped", children: "Recording stopped" }) : null,
|
|
11580
|
+
state === "processing" ? (
|
|
11581
|
+
// rec-processing: the host is told it is being made ready (the indicator says Processing…).
|
|
11582
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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." })
|
|
11583
|
+
) : null,
|
|
11584
|
+
state === "available" && canStart && meetingId !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("a", { className: "mx-meet__link", href: `/meetings/${meetingId}`, target: "_blank", rel: "noreferrer", children: "View recording" }) : null
|
|
11585
|
+
] });
|
|
11259
11586
|
}
|
|
11260
11587
|
function RoleBadge({
|
|
11261
11588
|
role,
|
|
@@ -11407,6 +11734,7 @@ function ControlBar({ onLeave }) {
|
|
|
11407
11734
|
const controls = useHostControls();
|
|
11408
11735
|
const queue = useHandQueue();
|
|
11409
11736
|
const micUnavailable = useLocalMedia().microphone.unavailable;
|
|
11737
|
+
const live = useLivePolicy();
|
|
11410
11738
|
const unread = useUnreadChat(panel === "chat");
|
|
11411
11739
|
const [whiteboard, setWhiteboard] = (0, import_react10.useState)(false);
|
|
11412
11740
|
const app = useMeetAppPanel();
|
|
@@ -11474,7 +11802,7 @@ function ControlBar({ onLeave }) {
|
|
|
11474
11802
|
panel !== "people" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(KnockNotice, { onView: () => setPanel("people") }) : null,
|
|
11475
11803
|
controls.notice !== null && panel !== "people" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Toast, { text: controls.notice.text, failed: controls.notice.failed, onDone: controls.clearNotice }) : null,
|
|
11476
11804
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__controls", children: [
|
|
11477
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
11805
|
+
live.micLocked ? null : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
11478
11806
|
ControlButton,
|
|
11479
11807
|
{
|
|
11480
11808
|
label: micUnavailable !== null ? `Unmute \u2014 ${micUnavailable.remedy}` : room.local?.micEnabled === true ? "Mute (\u2318D)" : "Unmute (\u2318D)",
|
|
@@ -11483,7 +11811,7 @@ function ControlBar({ onLeave }) {
|
|
|
11483
11811
|
children: room.local?.micEnabled === true ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MicIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MicOffIcon, {})
|
|
11484
11812
|
}
|
|
11485
11813
|
),
|
|
11486
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
11814
|
+
live.cameraLocked ? null : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
11487
11815
|
ControlButton,
|
|
11488
11816
|
{
|
|
11489
11817
|
label: room.local?.cameraEnabled === true ? "Stop video (\u2318E)" : "Start video (\u2318E)",
|
|
@@ -11492,6 +11820,7 @@ function ControlBar({ onLeave }) {
|
|
|
11492
11820
|
children: room.local?.cameraEnabled === true ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CameraIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CameraOffIcon, {})
|
|
11493
11821
|
}
|
|
11494
11822
|
),
|
|
11823
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CaptionsControl, {}),
|
|
11495
11824
|
room.canShareScreen || room.local?.screenSharing === true ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
11496
11825
|
ControlButton,
|
|
11497
11826
|
{
|
|
@@ -11556,6 +11885,7 @@ function ControlBar({ onLeave }) {
|
|
|
11556
11885
|
onOpen: openActivity
|
|
11557
11886
|
}
|
|
11558
11887
|
),
|
|
11888
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(TakeNotesControl, {}),
|
|
11559
11889
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(AiControl, { active: panel === "ai", onToggle: () => toggle("ai") }),
|
|
11560
11890
|
recording.canStart ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ControlButton, { label: "Start recording", onClick: () => void recording.start(), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RecordIcon, {}) }) : null,
|
|
11561
11891
|
recording.canStop ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ControlButton, { label: "Stop recording", active: true, onClick: () => void recording.stop(), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RecordIcon, {}) }) : null,
|
|
@@ -11611,6 +11941,23 @@ function ControlBar({ onLeave }) {
|
|
|
11611
11941
|
]
|
|
11612
11942
|
}
|
|
11613
11943
|
),
|
|
11944
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
11945
|
+
MenuItem,
|
|
11946
|
+
{
|
|
11947
|
+
onSelect: () => {
|
|
11948
|
+
setPopover("none");
|
|
11949
|
+
void controls.run({
|
|
11950
|
+
action: "set_av_lock",
|
|
11951
|
+
value: snapshot?.livePolicy.avLock === "both" ? "off" : "both"
|
|
11952
|
+
});
|
|
11953
|
+
},
|
|
11954
|
+
children: [
|
|
11955
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MicOffIcon, { width: 16, height: 16 }),
|
|
11956
|
+
" ",
|
|
11957
|
+
snapshot?.livePolicy.avLock === "both" ? "Turn on attendees' microphone and camera" : "Turn off attendees' microphone and camera"
|
|
11958
|
+
]
|
|
11959
|
+
}
|
|
11960
|
+
),
|
|
11614
11961
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
11615
11962
|
MenuItem,
|
|
11616
11963
|
{
|
|
@@ -11938,9 +12285,45 @@ function MeetingOverlays() {
|
|
|
11938
12285
|
const reactions = useReactions();
|
|
11939
12286
|
const { notice, dismiss } = useModerationNotice();
|
|
11940
12287
|
const room = useRoom();
|
|
12288
|
+
const live = useLivePolicy();
|
|
12289
|
+
const snapshot = useMeetSnapshot();
|
|
12290
|
+
const controls = useHostControls();
|
|
12291
|
+
const isHostNow = useRole() === "host";
|
|
11941
12292
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
|
|
11942
12293
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ReactionOverlay, { reactions }),
|
|
11943
12294
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MediaNotices, {}),
|
|
12295
|
+
snapshot !== null && snapshot.phase === "in_call" && snapshot.participants.length >= 2 && !snapshot.participants.some((p) => p.role === "host" || p.role === "cohost") ? (
|
|
12296
|
+
// meeting-without-host: the call goes on; host-only controls are simply not on screen.
|
|
12297
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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." })
|
|
12298
|
+
) : null,
|
|
12299
|
+
live.consent !== null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-meet__sheet-backdrop", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__sheet", role: "alertdialog", "aria-label": "This meeting is being recorded", "data-meet-notice": "recording-consent", children: [
|
|
12300
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { className: "mx-meet__panel-title", children: "This meeting is being recorded" }),
|
|
12301
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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." }),
|
|
12302
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__menu-actions", children: [
|
|
12303
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__send", onClick: live.agree, children: live.consent === "ok_or_leave" ? "OK" : "I agree" }),
|
|
12304
|
+
live.consent === "ok_or_leave" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "mx-meet__link", onClick: () => void room.leave(), children: "Leave meeting" }) : null
|
|
12305
|
+
] })
|
|
12306
|
+
] }) }) : null,
|
|
12307
|
+
snapshot?.canReclaim === true && !isHostNow ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__notice-bar", role: "status", "data-meet-notice": "host-reclaim", children: [
|
|
12308
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "You started this meeting. Take back the host?" }),
|
|
12309
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
12310
|
+
"button",
|
|
12311
|
+
{
|
|
12312
|
+
type: "button",
|
|
12313
|
+
className: "mx-meet__primary mx-meet__primary--small",
|
|
12314
|
+
onClick: () => void controls.run({ action: "reclaim_host" }),
|
|
12315
|
+
children: "Reclaim host"
|
|
12316
|
+
}
|
|
12317
|
+
)
|
|
12318
|
+
] }) : null,
|
|
12319
|
+
live.micLocked || live.cameraLocked ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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,
|
|
12320
|
+
live.timeWarning && live.minutesLeft !== null && live.minutesLeft > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "time-warning", children: [
|
|
12321
|
+
"This meeting will end in ",
|
|
12322
|
+
live.minutesLeft,
|
|
12323
|
+
" ",
|
|
12324
|
+
live.minutesLeft === 1 ? "minute" : "minutes",
|
|
12325
|
+
" (time limit)."
|
|
12326
|
+
] }) : null,
|
|
11944
12327
|
notice !== null && notice.kind !== "removed" ? (
|
|
11945
12328
|
// A host's request ("the host is asking you to unmute"), not an error:
|
|
11946
12329
|
// an assertive status, so it is announced at once without being filed
|
|
@@ -12031,6 +12414,7 @@ function ChatPanel({
|
|
|
12031
12414
|
}) {
|
|
12032
12415
|
const snapshot = useMeetSnapshot();
|
|
12033
12416
|
const room = useRoom();
|
|
12417
|
+
const live = useLivePolicy();
|
|
12034
12418
|
const [draft, setDraft] = (0, import_react10.useState)("");
|
|
12035
12419
|
const [recipient, setRecipient] = (0, import_react10.useState)(to);
|
|
12036
12420
|
const listRef = (0, import_react10.useRef)(null);
|
|
@@ -12055,7 +12439,7 @@ function ChatPanel({
|
|
|
12055
12439
|
snapshot?.chatLoaded !== true && messages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("li", { className: "mx-meet__panel-note", children: "Reading earlier messages\u2026" }) : messages.length === 0 && snapshot?.chatHistoryError == null ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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,
|
|
12056
12440
|
messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChatLine, { message, local, people }, message.id))
|
|
12057
12441
|
] }),
|
|
12058
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
12442
|
+
!live.canChat ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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__ */ (0, import_jsx_runtime6.jsxs)(
|
|
12059
12443
|
"form",
|
|
12060
12444
|
{
|
|
12061
12445
|
className: "mx-meet__composer mx-meet__composer--chat",
|
|
@@ -12427,6 +12811,13 @@ function PersonMenu({
|
|
|
12427
12811
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PeopleIcon, { width: 16, height: 16 }),
|
|
12428
12812
|
" Make co-host"
|
|
12429
12813
|
] }) : null,
|
|
12814
|
+
!participant.isAgent && participant.role !== "cohost" && participant.role !== "host" ? participant.role === "presenter" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(MenuItem, { onSelect: () => act({ action: "revoke_presenter", identity: participant.identity }), children: [
|
|
12815
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ScreenShareIcon, { width: 16, height: 16 }),
|
|
12816
|
+
" Remove presenter"
|
|
12817
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(MenuItem, { onSelect: () => act({ action: "make_presenter", identity: participant.identity }), children: [
|
|
12818
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ScreenShareIcon, { width: 16, height: 16 }),
|
|
12819
|
+
" Make presenter"
|
|
12820
|
+
] }) : null,
|
|
12430
12821
|
!participant.isAgent ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
12431
12822
|
MenuItem,
|
|
12432
12823
|
{
|
|
@@ -12932,8 +13323,19 @@ function Captions({ lines = 3 }) {
|
|
|
12932
13323
|
const ai = useMeetAi();
|
|
12933
13324
|
const visible = ai.transcript.slice(-lines);
|
|
12934
13325
|
const translation = useCaptionTranslation(visible);
|
|
12935
|
-
|
|
12936
|
-
|
|
13326
|
+
const captions = useCaptions();
|
|
13327
|
+
if (!captions.on) return null;
|
|
13328
|
+
if (captions.state === "unavailable") {
|
|
13329
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-meet__captions", role: "status", "data-meet-notice": "captions-unavailable", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { className: "mx-meet__caption", children: [
|
|
13330
|
+
"Captions are unavailable. ",
|
|
13331
|
+
captions.reason,
|
|
13332
|
+
captions.remedy !== null ? ` ${captions.remedy}` : ""
|
|
13333
|
+
] }) });
|
|
13334
|
+
}
|
|
13335
|
+
if (visible.length === 0) {
|
|
13336
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mx-meet__captions", "aria-live": "polite", "aria-label": "Live captions", "data-meet-caption": captions.state, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__caption mx-meet__caption--interim", children: captions.state === "starting" ? "Starting captions. They appear as people speak." : "Captions are on. They appear as people speak." }) });
|
|
13337
|
+
}
|
|
13338
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "mx-meet__captions", "aria-live": "polite", "aria-label": "Live captions", "data-meet-caption": "live", children: [
|
|
12937
13339
|
visible.map((line) => {
|
|
12938
13340
|
const translated = translation.language === null ? void 0 : translation.translations[line.id];
|
|
12939
13341
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
@@ -12956,7 +13358,7 @@ function Captions({ lines = 3 }) {
|
|
|
12956
13358
|
function ConsentNotice() {
|
|
12957
13359
|
const notice = useConsentNotice();
|
|
12958
13360
|
if (notice === null) return null;
|
|
12959
|
-
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__consent", role: "status", children: notice });
|
|
13361
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "mx-meet__consent", role: "status", "data-meet-notice": "notes-started", children: notice });
|
|
12960
13362
|
}
|
|
12961
13363
|
function AttendanceNotice() {
|
|
12962
13364
|
const media = useLocalMedia();
|