@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.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,6 +2419,55 @@ 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
|
|
2384
2472
|
var IDLE_MEDIA_REGION = {
|
|
2385
2473
|
permission: { microphone: "unknown", camera: "unknown" },
|
|
@@ -3506,6 +3594,8 @@ function planFromRow(row) {
|
|
|
3506
3594
|
minutes: num2(row["minutes"]) ?? 0,
|
|
3507
3595
|
openedAt: str(row["opened_at"]),
|
|
3508
3596
|
endsAt: str(row["ends_at"]),
|
|
3597
|
+
closingAt: str(row["closing_at"]),
|
|
3598
|
+
moveMode: row["move_mode"] === "auto" ? "auto" : "invite",
|
|
3509
3599
|
closedAt: str(row["closed_at"]),
|
|
3510
3600
|
broadcasts: (Array.isArray(row["broadcasts"]) ? row["broadcasts"] : []).map(broadcastFrom).filter((b) => b !== null)
|
|
3511
3601
|
};
|
|
@@ -3657,9 +3747,17 @@ function autoAssign(identities, rooms, exclude = []) {
|
|
|
3657
3747
|
return out;
|
|
3658
3748
|
}
|
|
3659
3749
|
function breakoutRemainingMs(plan, now) {
|
|
3660
|
-
if (plan === null || plan.state !== "open"
|
|
3661
|
-
const
|
|
3662
|
-
|
|
3750
|
+
if (plan === null || plan.state !== "open") return null;
|
|
3751
|
+
const ends = [plan.endsAt, plan.closingAt].map((stamp) => stamp === null ? Number.NaN : Date.parse(stamp)).filter((time) => !Number.isNaN(time));
|
|
3752
|
+
if (ends.length === 0) return null;
|
|
3753
|
+
return Math.max(0, Math.min(...ends) - now);
|
|
3754
|
+
}
|
|
3755
|
+
var BREAKOUT_COUNTDOWN_MS = 3e4;
|
|
3756
|
+
function breakoutClosingMs(plan, now) {
|
|
3757
|
+
if (plan === null || plan.state !== "open") return null;
|
|
3758
|
+
const remaining = breakoutRemainingMs(plan, now);
|
|
3759
|
+
if (remaining === null) return null;
|
|
3760
|
+
return plan.closingAt !== null || remaining <= BREAKOUT_COUNTDOWN_MS ? remaining : null;
|
|
3663
3761
|
}
|
|
3664
3762
|
function defaultCaptionLanguage(settings, viewerLanguage) {
|
|
3665
3763
|
const configured = settings?.captionTranslationLanguage.trim() ?? "";
|
|
@@ -3836,6 +3934,11 @@ var EMPTY = {
|
|
|
3836
3934
|
presenter: null,
|
|
3837
3935
|
waitingCount: 0,
|
|
3838
3936
|
leavePolicy: DEFAULT_LEAVE_POLICY,
|
|
3937
|
+
livePolicy: DEFAULT_LIVE_POLICY,
|
|
3938
|
+
canReclaim: false,
|
|
3939
|
+
recordingConsentGiven: false,
|
|
3940
|
+
sessionStartedAt: null,
|
|
3941
|
+
mutedByHost: false,
|
|
3839
3942
|
moderationNotice: null,
|
|
3840
3943
|
actionNotice: null,
|
|
3841
3944
|
privateChatRequest: null,
|
|
@@ -3845,6 +3948,8 @@ var EMPTY = {
|
|
|
3845
3948
|
names: {},
|
|
3846
3949
|
wrapUp: null,
|
|
3847
3950
|
noteTakerRefusal: null,
|
|
3951
|
+
captionsOn: false,
|
|
3952
|
+
captionsRefusal: null,
|
|
3848
3953
|
media: IDLE_MEDIA,
|
|
3849
3954
|
mediaRegion: IDLE_MEDIA_REGION,
|
|
3850
3955
|
failure: null,
|
|
@@ -4049,7 +4154,12 @@ function createMeetStore(initial = {}) {
|
|
|
4049
4154
|
if (current.enabled === next.enabled && current.unavailable?.code === next.unavailable?.code && current.unavailable?.reason === next.unavailable?.reason && current.unavailable?.message === next.unavailable?.message) {
|
|
4050
4155
|
return;
|
|
4051
4156
|
}
|
|
4052
|
-
commit({
|
|
4157
|
+
commit({
|
|
4158
|
+
...state,
|
|
4159
|
+
media: { ...state.media, [kind]: next },
|
|
4160
|
+
// The person turned their own microphone on again: the host's mute is over.
|
|
4161
|
+
...kind === "microphone" && next.enabled && state.mutedByHost ? { mutedByHost: false } : {}
|
|
4162
|
+
});
|
|
4053
4163
|
},
|
|
4054
4164
|
reset() {
|
|
4055
4165
|
const phase = nextPhase(state.phase, { type: "reset", teardown: true }) ?? EMPTY.phase;
|
|
@@ -4111,6 +4221,7 @@ function applyDoorRow(store, attendee) {
|
|
|
4111
4221
|
function decidedState(state) {
|
|
4112
4222
|
return WAITING_ADMISSION_STATES.includes(state) ? "expired" : state;
|
|
4113
4223
|
}
|
|
4224
|
+
var LIVE_POLICY_POLL_MS = 3e3;
|
|
4114
4225
|
function createMeetingSession(options) {
|
|
4115
4226
|
const { manager, repository, store, identity } = options;
|
|
4116
4227
|
let channel = null;
|
|
@@ -4252,6 +4363,46 @@ function createMeetingSession(options) {
|
|
|
4252
4363
|
stopWaiting();
|
|
4253
4364
|
}
|
|
4254
4365
|
}
|
|
4366
|
+
let policyTimer = null;
|
|
4367
|
+
let policyReading = false;
|
|
4368
|
+
async function readLivePolicy() {
|
|
4369
|
+
const meeting = store.snapshot().meeting;
|
|
4370
|
+
const pass = options.resolvePass?.() ?? null;
|
|
4371
|
+
if (meeting === null || pass === null || policyReading) return;
|
|
4372
|
+
policyReading = true;
|
|
4373
|
+
try {
|
|
4374
|
+
const response = await options.api.request({
|
|
4375
|
+
path: `${MEET_ROUTES.state}?meeting_id=${encodeURIComponent(meeting.id)}`,
|
|
4376
|
+
method: "GET",
|
|
4377
|
+
requireAuth: false,
|
|
4378
|
+
operation: "meet.state(policy)",
|
|
4379
|
+
headers: { [MEETING_PASS_HEADER]: pass }
|
|
4380
|
+
});
|
|
4381
|
+
if (!response.ok) return;
|
|
4382
|
+
const read = await response.json();
|
|
4383
|
+
const next = livePolicyFrom(read.live_policy);
|
|
4384
|
+
const started = read.session_started_at ? Date.parse(read.session_started_at) : NaN;
|
|
4385
|
+
const snapshot = store.snapshot();
|
|
4386
|
+
const startedAt = Number.isFinite(started) ? started : snapshot.sessionStartedAt;
|
|
4387
|
+
if (!samePolicy(snapshot.livePolicy, next) || startedAt !== snapshot.sessionStartedAt) {
|
|
4388
|
+
store.patch({ livePolicy: next, sessionStartedAt: startedAt });
|
|
4389
|
+
}
|
|
4390
|
+
} catch {
|
|
4391
|
+
} finally {
|
|
4392
|
+
policyReading = false;
|
|
4393
|
+
}
|
|
4394
|
+
}
|
|
4395
|
+
function syncLivePolicyTimer() {
|
|
4396
|
+
const inCall = store.snapshot().phase === "in_call";
|
|
4397
|
+
if (inCall && policyTimer === null) {
|
|
4398
|
+
void readLivePolicy();
|
|
4399
|
+
policyTimer = setInterval(() => void readLivePolicy(), LIVE_POLICY_POLL_MS);
|
|
4400
|
+
} else if (!inCall && policyTimer !== null) {
|
|
4401
|
+
clearInterval(policyTimer);
|
|
4402
|
+
policyTimer = null;
|
|
4403
|
+
}
|
|
4404
|
+
}
|
|
4405
|
+
const unsubscribePolicy = store.subscribe(syncLivePolicyTimer);
|
|
4255
4406
|
async function readState2() {
|
|
4256
4407
|
if (reading !== null) return reading;
|
|
4257
4408
|
const meeting = store.snapshot().meeting;
|
|
@@ -4638,6 +4789,9 @@ function createMeetingSession(options) {
|
|
|
4638
4789
|
},
|
|
4639
4790
|
dispose() {
|
|
4640
4791
|
stopWaiting();
|
|
4792
|
+
unsubscribePolicy();
|
|
4793
|
+
if (policyTimer !== null) clearInterval(policyTimer);
|
|
4794
|
+
policyTimer = null;
|
|
4641
4795
|
channel?.close();
|
|
4642
4796
|
channel = null;
|
|
4643
4797
|
meetingChannel?.close();
|
|
@@ -5229,10 +5383,11 @@ function moderationText(kind, payload) {
|
|
|
5229
5383
|
switch (kind) {
|
|
5230
5384
|
case "muted": {
|
|
5231
5385
|
const source = text2(payload["source"]) ?? "microphone";
|
|
5232
|
-
|
|
5386
|
+
const who = payload["by_role"] === "host" || payload["by_role"] === "cohost" ? "the host" : by;
|
|
5387
|
+
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
5388
|
}
|
|
5234
5389
|
case "ask-unmute":
|
|
5235
|
-
return `${by}
|
|
5390
|
+
return `${by === "The host" ? "The host" : by} asked you to unmute.`;
|
|
5236
5391
|
case "removed":
|
|
5237
5392
|
return `${by} removed you from this meeting.`;
|
|
5238
5393
|
default:
|
|
@@ -5258,7 +5413,7 @@ function applyModeration(store, payload, now) {
|
|
|
5258
5413
|
grants: { ...snapshot.grants, role },
|
|
5259
5414
|
moderationNotice: {
|
|
5260
5415
|
kind: "role",
|
|
5261
|
-
text: role === "host" ? "You are now the host." : role === "cohost" ? "You
|
|
5416
|
+
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
5417
|
at: now()
|
|
5263
5418
|
}
|
|
5264
5419
|
});
|
|
@@ -5270,7 +5425,10 @@ function applyModeration(store, payload, now) {
|
|
|
5270
5425
|
case "removed": {
|
|
5271
5426
|
const sentence = moderationText(kind, payload);
|
|
5272
5427
|
if (sentence === null) return ignored("a moderation event with no sentence");
|
|
5273
|
-
store.patch({
|
|
5428
|
+
store.patch({
|
|
5429
|
+
moderationNotice: { kind, text: sentence, at: now() },
|
|
5430
|
+
...kind === "muted" && (text2(payload["source"]) ?? "microphone") === "microphone" ? { mutedByHost: true } : {}
|
|
5431
|
+
});
|
|
5274
5432
|
return HANDLED;
|
|
5275
5433
|
}
|
|
5276
5434
|
default:
|
|
@@ -5309,7 +5467,7 @@ function exactNoteTakerParticipant(meeting, participants) {
|
|
|
5309
5467
|
}
|
|
5310
5468
|
function noteTakerStatus(args) {
|
|
5311
5469
|
const present = exactNoteTakerParticipant(args.meeting, args.participants) !== null;
|
|
5312
|
-
const raw = args.meeting?.metadata[NOTE_TAKER_METADATA_KEY];
|
|
5470
|
+
const raw = args.meeting?.metadata?.[NOTE_TAKER_METADATA_KEY];
|
|
5313
5471
|
const row = typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : {};
|
|
5314
5472
|
const recorded = readState(row["state"]);
|
|
5315
5473
|
const detail = typeof row["reason"] === "string" && row["reason"].length > 0 ? row["reason"] : null;
|
|
@@ -5786,6 +5944,7 @@ function createRoomEngine(options) {
|
|
|
5786
5944
|
lastServerUrl = token.serverUrl;
|
|
5787
5945
|
connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;
|
|
5788
5946
|
if (token.leavePolicy !== void 0) store.patch({ leavePolicy: token.leavePolicy });
|
|
5947
|
+
store.patch({ canReclaim: token.canReclaim === true });
|
|
5789
5948
|
store.patch({
|
|
5790
5949
|
localIdentity: token.identity,
|
|
5791
5950
|
breakoutRoomId: token.breakoutRoomId ?? null,
|
|
@@ -6285,8 +6444,12 @@ var MODERATION_ACTIONS = [
|
|
|
6285
6444
|
"lower_hand",
|
|
6286
6445
|
"lower_all_hands",
|
|
6287
6446
|
"make_cohost",
|
|
6447
|
+
"make_presenter",
|
|
6448
|
+
"revoke_presenter",
|
|
6449
|
+
"set_av_lock",
|
|
6288
6450
|
"revoke_cohost",
|
|
6289
6451
|
"transfer_host",
|
|
6452
|
+
"reclaim_host",
|
|
6290
6453
|
"rename",
|
|
6291
6454
|
"remove",
|
|
6292
6455
|
"report"
|
|
@@ -7324,11 +7487,13 @@ import { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2 } from
|
|
|
7324
7487
|
|
|
7325
7488
|
// src/core/observation.ts
|
|
7326
7489
|
function observedRole(role, standing) {
|
|
7327
|
-
if (role === "host" || role === "cohost") return role;
|
|
7490
|
+
if (role === "host" || role === "cohost" || role === "presenter") return role;
|
|
7328
7491
|
return isLinkFollower(standing) ? "guest" : "participant";
|
|
7329
7492
|
}
|
|
7330
|
-
function observedDevice(track, permission = "unknown") {
|
|
7493
|
+
function observedDevice(track, permission = "unknown", held = null) {
|
|
7494
|
+
if (held === "locked") return "locked";
|
|
7331
7495
|
if (track.enabled) return "on";
|
|
7496
|
+
if (held === "muted_by_host") return "muted_by_host";
|
|
7332
7497
|
const reason = deviceUnavailableReason(track, permission);
|
|
7333
7498
|
return reason === null ? "muted_by_me" : `unavailable:${reason}`;
|
|
7334
7499
|
}
|
|
@@ -7365,10 +7530,20 @@ function observe(input) {
|
|
|
7365
7530
|
recording: snapshot?.recording === "recording" || snapshot?.recording === "stopping",
|
|
7366
7531
|
// S6c: the autoplay region is known only inside the room (no remote audio exists elsewhere).
|
|
7367
7532
|
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
|
|
7533
|
+
camera: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.camera, snapshot.mediaRegion.permission.camera, lockedFor(snapshot, "camera", inCall)) : null,
|
|
7534
|
+
microphone: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.microphone, snapshot.mediaRegion.permission.microphone, lockedFor(snapshot, "microphone", inCall)) : null
|
|
7370
7535
|
};
|
|
7371
7536
|
}
|
|
7537
|
+
function lockedFor(snapshot, kind, inCall) {
|
|
7538
|
+
if (!inCall) return null;
|
|
7539
|
+
const role = effectiveRole(snapshot);
|
|
7540
|
+
if (isLockedFor(snapshot.livePolicy, role, kind)) return "locked";
|
|
7541
|
+
if (snapshot.livePolicy.recordingConsent === "view_only_until_agree" && consentPending(snapshot.livePolicy, role, snapshot.recording === "recording", snapshot.recordingConsentGiven)) {
|
|
7542
|
+
return "locked";
|
|
7543
|
+
}
|
|
7544
|
+
if (kind === "microphone" && snapshot.mutedByHost) return "muted_by_host";
|
|
7545
|
+
return null;
|
|
7546
|
+
}
|
|
7372
7547
|
function staticObservation(phase, reason = null) {
|
|
7373
7548
|
return {
|
|
7374
7549
|
phase,
|
|
@@ -8558,6 +8733,45 @@ function useMeetAi() {
|
|
|
8558
8733
|
}, [ai, meeting, dispatchNoteTaker])
|
|
8559
8734
|
};
|
|
8560
8735
|
}
|
|
8736
|
+
function useCaptions() {
|
|
8737
|
+
const host = useMeetHost();
|
|
8738
|
+
const snapshot = useMeetSnapshot();
|
|
8739
|
+
const ai = useMeetAi();
|
|
8740
|
+
const on = snapshot?.captionsOn === true;
|
|
8741
|
+
const refusal2 = snapshot?.captionsRefusal ?? null;
|
|
8742
|
+
const taker = ai.noteTaker;
|
|
8743
|
+
const meeting = snapshot?.meeting ?? null;
|
|
8744
|
+
const request = useCallback2(() => {
|
|
8745
|
+
if (host === null || meeting === null) return;
|
|
8746
|
+
void host.ai.requestCaptions({ meetingId: meeting.id });
|
|
8747
|
+
}, [host, meeting]);
|
|
8748
|
+
const toggle = useCallback2(() => {
|
|
8749
|
+
if (host === null) return;
|
|
8750
|
+
const next = !on;
|
|
8751
|
+
host.store.patch({ captionsOn: next, captionsRefusal: null });
|
|
8752
|
+
if (next && taker.state !== "joining" && taker.state !== "transcribing") request();
|
|
8753
|
+
}, [host, on, taker.state, request]);
|
|
8754
|
+
let state = "off";
|
|
8755
|
+
let reason = null;
|
|
8756
|
+
let remedy = null;
|
|
8757
|
+
if (on) {
|
|
8758
|
+
if (taker.state === "transcribing" && taker.present) {
|
|
8759
|
+
state = "live";
|
|
8760
|
+
} else if (taker.state === "joining") {
|
|
8761
|
+
state = "starting";
|
|
8762
|
+
} else if (refusal2 !== null && refusal2.reason.length > 0) {
|
|
8763
|
+
state = "unavailable";
|
|
8764
|
+
reason = refusal2.reason;
|
|
8765
|
+
remedy = refusal2.remedy.length > 0 ? refusal2.remedy : null;
|
|
8766
|
+
} else if (taker.state === "ended" || taker.state === "handoff_pending") {
|
|
8767
|
+
state = "unavailable";
|
|
8768
|
+
reason = noteTakerLabel(taker);
|
|
8769
|
+
} else {
|
|
8770
|
+
state = "starting";
|
|
8771
|
+
}
|
|
8772
|
+
}
|
|
8773
|
+
return { on, state, reason, remedy, toggle };
|
|
8774
|
+
}
|
|
8561
8775
|
function useEndMeeting() {
|
|
8562
8776
|
const host = useMeetHost();
|
|
8563
8777
|
const snapshot = useMeetSnapshot();
|
|
@@ -8595,7 +8809,7 @@ function useConsentNotice() {
|
|
|
8595
8809
|
const snapshot = useMeetSnapshot();
|
|
8596
8810
|
const agent = noteTakerParticipant(snapshot?.participants ?? []);
|
|
8597
8811
|
if (agent === null) return null;
|
|
8598
|
-
return
|
|
8812
|
+
return `AI notes are on. ${agent.displayName} is in this meeting, transcribing what is said and writing notes everyone here can read.`;
|
|
8599
8813
|
}
|
|
8600
8814
|
function useMediaSupervisor() {
|
|
8601
8815
|
const host = useMeetHost();
|
|
@@ -9059,6 +9273,38 @@ function useGuestRecordAccess(meeting) {
|
|
|
9059
9273
|
)
|
|
9060
9274
|
};
|
|
9061
9275
|
}
|
|
9276
|
+
function useLivePolicy() {
|
|
9277
|
+
const snapshot = useMeetSnapshot();
|
|
9278
|
+
const role = useRole();
|
|
9279
|
+
const [now, setNow] = useState2(() => Date.now());
|
|
9280
|
+
const limited = (snapshot?.livePolicy.timeLimitMinutes ?? 0) > 0;
|
|
9281
|
+
useEffect3(() => {
|
|
9282
|
+
if (!limited) return void 0;
|
|
9283
|
+
const timer = setInterval(() => setNow(Date.now()), 1e3);
|
|
9284
|
+
return () => clearInterval(timer);
|
|
9285
|
+
}, [limited]);
|
|
9286
|
+
const policy = snapshot?.livePolicy ?? DEFAULT_LIVE_POLICY;
|
|
9287
|
+
const left = minutesLeft(policy, snapshot?.sessionStartedAt ?? null, now);
|
|
9288
|
+
const host = useMeetHost();
|
|
9289
|
+
const pending = consentPending(policy, role, snapshot?.recording === "recording", snapshot?.recordingConsentGiven === true);
|
|
9290
|
+
const viewOnly = pending && policy.recordingConsent === "view_only_until_agree";
|
|
9291
|
+
const idle = snapshot !== null && snapshot.recording !== "recording" && snapshot.recording !== "starting" && snapshot.recording !== "stopping";
|
|
9292
|
+
const given = snapshot?.recordingConsentGiven === true;
|
|
9293
|
+
const store = host?.store ?? null;
|
|
9294
|
+
useEffect3(() => {
|
|
9295
|
+
if (idle && given) store?.patch({ recordingConsentGiven: false });
|
|
9296
|
+
}, [idle, given, store]);
|
|
9297
|
+
return {
|
|
9298
|
+
canChat: canChat(policy, role),
|
|
9299
|
+
chatMode: policy.chatMode,
|
|
9300
|
+
micLocked: isLockedFor(policy, role, "microphone") || viewOnly,
|
|
9301
|
+
cameraLocked: isLockedFor(policy, role, "camera") || viewOnly,
|
|
9302
|
+
consent: pending ? policy.recordingConsent === "ok_or_leave" ? "ok_or_leave" : "view_only_until_agree" : null,
|
|
9303
|
+
agree: () => store?.patch({ recordingConsentGiven: true }),
|
|
9304
|
+
minutesLeft: left,
|
|
9305
|
+
timeWarning: left !== null && left <= policy.timeLimitWarningMinutes
|
|
9306
|
+
};
|
|
9307
|
+
}
|
|
9062
9308
|
|
|
9063
9309
|
// src/react/skin-root.tsx
|
|
9064
9310
|
import { useEffect as useEffect8 } from "react";
|
|
@@ -9833,6 +10079,18 @@ function SparklesIcon(props) {
|
|
|
9833
10079
|
/* @__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
10080
|
] });
|
|
9835
10081
|
}
|
|
10082
|
+
function CaptionsIcon(props) {
|
|
10083
|
+
return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
|
|
10084
|
+
/* @__PURE__ */ jsx4("rect", { x: "3", y: "5", width: "18", height: "14", rx: "2" }),
|
|
10085
|
+
/* @__PURE__ */ jsx4("path", { d: "M10 10.5a2 2 0 1 0 0 3M16 10.5a2 2 0 1 0 0 3" })
|
|
10086
|
+
] });
|
|
10087
|
+
}
|
|
10088
|
+
function NotesIcon(props) {
|
|
10089
|
+
return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
|
|
10090
|
+
/* @__PURE__ */ jsx4("path", { d: "M4 20l1-4L16.5 4.5a2.1 2.1 0 0 1 3 3L8 19l-4 1Z" }),
|
|
10091
|
+
/* @__PURE__ */ jsx4("path", { d: "M14 7l3 3" })
|
|
10092
|
+
] });
|
|
10093
|
+
}
|
|
9836
10094
|
function LockIcon(props) {
|
|
9837
10095
|
return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
|
|
9838
10096
|
/* @__PURE__ */ jsx4("rect", { x: "4", y: "10", width: "16", height: "11", rx: "2" }),
|
|
@@ -10739,7 +10997,7 @@ function BreakoutRun({ plan }) {
|
|
|
10739
10997
|
type: "button",
|
|
10740
10998
|
className: "mx-meet__primary mx-meet__primary--small",
|
|
10741
10999
|
onClick: () => void act({ action: "breakout_close" }).then(show),
|
|
10742
|
-
children: "
|
|
11000
|
+
children: "Close rooms"
|
|
10743
11001
|
}
|
|
10744
11002
|
) : null
|
|
10745
11003
|
] });
|
|
@@ -10757,9 +11015,14 @@ function BreakoutBanner() {
|
|
|
10757
11015
|
const { collab, act, moveTo, localIdentity, breakoutRoomId } = useCollab();
|
|
10758
11016
|
const plan = collab?.breakout ?? null;
|
|
10759
11017
|
const open = plan?.state === "open" ? plan : null;
|
|
10760
|
-
const now = useNow(open?.endsAt != null);
|
|
11018
|
+
const now = useNow(open?.endsAt != null || open?.closingAt != null);
|
|
10761
11019
|
const remaining = breakoutRemainingMs(open, now);
|
|
11020
|
+
const closing = breakoutClosingMs(open, now);
|
|
10762
11021
|
const assigned = assignedRoom(open, localIdentity);
|
|
11022
|
+
const [later, setLater] = useState3(null);
|
|
11023
|
+
const [autoSince, setAutoSince] = useState3(null);
|
|
11024
|
+
const AUTO_MOVE_MS = 1e4;
|
|
11025
|
+
const autoLeft = open?.moveMode === "auto" && assigned !== null && autoSince?.id === open.id && later !== open.id ? Math.max(0, AUTO_MOVE_MS - (now - autoSince.at)) : null;
|
|
10763
11026
|
const [dismissedBroadcast, setDismissedBroadcast] = useState3(null);
|
|
10764
11027
|
const [failure, setFailure] = useState3(null);
|
|
10765
11028
|
const expireAsked = useRef5(null);
|
|
@@ -10772,6 +11035,19 @@ function BreakoutBanner() {
|
|
|
10772
11035
|
if ((here ?? null) === request.roomId) return;
|
|
10773
11036
|
void moveTo(request.roomId).then((result) => setFailure(result.ok ? null : result.message));
|
|
10774
11037
|
}, [moveAt, host, moveTo]);
|
|
11038
|
+
const joinAssigned = async () => {
|
|
11039
|
+
if (assigned === null) return;
|
|
11040
|
+
const said = await act({ action: "breakout_join", roomId: assigned.id });
|
|
11041
|
+
const result = said.ok ? await moveTo(assigned.id) : said;
|
|
11042
|
+
setFailure(result.ok ? null : result.message);
|
|
11043
|
+
};
|
|
11044
|
+
useEffect5(() => {
|
|
11045
|
+
if (open === null || open.moveMode !== "auto" || assigned === null || breakoutRoomId !== null) return;
|
|
11046
|
+
if (autoSince?.id !== open.id) setAutoSince({ id: open.id, at: Date.now() });
|
|
11047
|
+
}, [open, assigned, breakoutRoomId, autoSince]);
|
|
11048
|
+
useEffect5(() => {
|
|
11049
|
+
if (autoLeft === 0) void joinAssigned();
|
|
11050
|
+
}, [autoLeft]);
|
|
10775
11051
|
useEffect5(() => {
|
|
10776
11052
|
if (open === null || remaining === null || remaining > 0) return;
|
|
10777
11053
|
const asked = expireAsked.current;
|
|
@@ -10785,27 +11061,25 @@ function BreakoutBanner() {
|
|
|
10785
11061
|
if (open === null && failure === null) return null;
|
|
10786
11062
|
return /* @__PURE__ */ jsxs2("div", { className: "mx-meet__breakout-banner", role: "status", children: [
|
|
10787
11063
|
failure !== null ? /* @__PURE__ */ jsx5("span", { className: "mx-meet__collab-error", children: failure }) : null,
|
|
11064
|
+
closing !== null ? /* @__PURE__ */ jsxs2("span", { role: "status", "data-meet-notice": "breakout-closing", children: [
|
|
11065
|
+
"Rooms closing in ",
|
|
11066
|
+
Math.ceil(closing / 1e3),
|
|
11067
|
+
room !== null ? " \u2014 you return to the main meeting" : ""
|
|
11068
|
+
] }) : null,
|
|
10788
11069
|
room !== null ? /* @__PURE__ */ jsxs2("span", { children: [
|
|
10789
11070
|
/* @__PURE__ */ jsx5("strong", { children: room.name }),
|
|
10790
|
-
remaining !== null ? ` \xB7 ${clock(remaining)} left` : "",
|
|
11071
|
+
remaining !== null && closing === null ? ` \xB7 ${clock(remaining)} left` : "",
|
|
10791
11072
|
/* @__PURE__ */ jsx5("button", { type: "button", className: "mx-meet__chip", onClick: () => void moveTo(null), children: "Return to main room" })
|
|
11073
|
+
] }) : assigned !== null && later !== open?.id ? /* @__PURE__ */ jsxs2("span", { children: [
|
|
11074
|
+
autoLeft !== null ? `You're being moved to ${assigned.name} in ${Math.ceil(autoLeft / 1e3)} s` : `You're invited to ${assigned.name}`,
|
|
11075
|
+
remaining !== null && closing === null ? ` \xB7 ${clock(remaining)} left` : "",
|
|
11076
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: "mx-meet__primary mx-meet__primary--small", onClick: () => void joinAssigned(), children: "Join room" }),
|
|
11077
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: "mx-meet__chip", onClick: () => setLater(open?.id ?? null), children: "Later" })
|
|
10792
11078
|
] }) : assigned !== null ? /* @__PURE__ */ jsxs2("span", { children: [
|
|
10793
|
-
|
|
10794
|
-
|
|
10795
|
-
|
|
10796
|
-
|
|
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,
|
|
11079
|
+
assigned.name,
|
|
11080
|
+
" is open",
|
|
11081
|
+
/* @__PURE__ */ jsx5("button", { type: "button", className: "mx-meet__chip", onClick: () => void joinAssigned(), children: "Join room" })
|
|
11082
|
+
] }) : open?.allowChoose ? /* @__PURE__ */ jsx5("span", { children: "Rooms are open \u2014 pick one under Activities." }) : null,
|
|
10809
11083
|
showBroadcast ? /* @__PURE__ */ jsxs2("span", { className: "mx-meet__breakout-broadcast", children: [
|
|
10810
11084
|
/* @__PURE__ */ jsxs2("strong", { children: [
|
|
10811
11085
|
broadcast.by,
|
|
@@ -10853,6 +11127,33 @@ function CaptionLanguageSettings() {
|
|
|
10853
11127
|
collab?.captionTranslationError != null ? /* @__PURE__ */ jsx5("p", { className: "mx-meet__panel-note mx-meet__collab-error", children: collab.captionTranslationError }) : null
|
|
10854
11128
|
] });
|
|
10855
11129
|
}
|
|
11130
|
+
function CaptionsControl() {
|
|
11131
|
+
const captions = useCaptions();
|
|
11132
|
+
return /* @__PURE__ */ jsx5(
|
|
11133
|
+
ControlButton,
|
|
11134
|
+
{
|
|
11135
|
+
label: captions.on ? "Turn off captions" : "Turn on captions",
|
|
11136
|
+
active: captions.on,
|
|
11137
|
+
pressed: captions.on,
|
|
11138
|
+
onClick: captions.toggle,
|
|
11139
|
+
children: /* @__PURE__ */ jsx5(CaptionsIcon, {})
|
|
11140
|
+
}
|
|
11141
|
+
);
|
|
11142
|
+
}
|
|
11143
|
+
function TakeNotesControl() {
|
|
11144
|
+
const ai = useMeetAi();
|
|
11145
|
+
if (!ai.canControlNoteTaker) return null;
|
|
11146
|
+
const running = ai.noteTaker.state === "joining" || ai.noteTaker.state === "transcribing";
|
|
11147
|
+
return /* @__PURE__ */ jsx5(
|
|
11148
|
+
ControlButton,
|
|
11149
|
+
{
|
|
11150
|
+
label: running ? "Stop taking notes" : "Take notes for me",
|
|
11151
|
+
active: running,
|
|
11152
|
+
onClick: () => void (running ? ai.stopNoteTaker() : ai.startNoteTaker()),
|
|
11153
|
+
children: /* @__PURE__ */ jsx5(NotesIcon, {})
|
|
11154
|
+
}
|
|
11155
|
+
);
|
|
11156
|
+
}
|
|
10856
11157
|
|
|
10857
11158
|
// src/skins/meet/components.tsx
|
|
10858
11159
|
import { Fragment, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -10903,6 +11204,7 @@ function ControlButton({
|
|
|
10903
11204
|
label,
|
|
10904
11205
|
active = false,
|
|
10905
11206
|
danger = false,
|
|
11207
|
+
pressed,
|
|
10906
11208
|
onClick,
|
|
10907
11209
|
children
|
|
10908
11210
|
}) {
|
|
@@ -10917,26 +11219,49 @@ function ControlButton({
|
|
|
10917
11219
|
].filter(Boolean).join(" "),
|
|
10918
11220
|
"aria-label": label,
|
|
10919
11221
|
title: label,
|
|
11222
|
+
...pressed !== void 0 ? { "aria-pressed": pressed } : {},
|
|
10920
11223
|
onClick,
|
|
10921
11224
|
children
|
|
10922
11225
|
}
|
|
10923
11226
|
);
|
|
10924
11227
|
}
|
|
10925
11228
|
function RecordingIndicator() {
|
|
10926
|
-
const { state, label } = useRecording();
|
|
10927
|
-
|
|
10928
|
-
|
|
10929
|
-
|
|
10930
|
-
|
|
10931
|
-
|
|
10932
|
-
|
|
10933
|
-
|
|
10934
|
-
|
|
10935
|
-
|
|
10936
|
-
|
|
10937
|
-
|
|
10938
|
-
|
|
10939
|
-
);
|
|
11229
|
+
const { state, label, canStart } = useRecording();
|
|
11230
|
+
const snapshot = useMeetSnapshot();
|
|
11231
|
+
const previous = useRef6(state);
|
|
11232
|
+
const [stoppedAt, setStoppedAt] = useState4(null);
|
|
11233
|
+
useEffect6(() => {
|
|
11234
|
+
const was = previous.current;
|
|
11235
|
+
previous.current = state;
|
|
11236
|
+
if ((was === "recording" || was === "starting") && state !== "recording" && state !== "starting") {
|
|
11237
|
+
setStoppedAt(Date.now());
|
|
11238
|
+
const timer = setTimeout(() => setStoppedAt(null), 1e4);
|
|
11239
|
+
return () => clearTimeout(timer);
|
|
11240
|
+
}
|
|
11241
|
+
return void 0;
|
|
11242
|
+
}, [state]);
|
|
11243
|
+
const meetingId = snapshot?.meeting?.id ?? null;
|
|
11244
|
+
if (label === null && stoppedAt === null) return null;
|
|
11245
|
+
return /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
11246
|
+
label !== null ? /* @__PURE__ */ jsxs3(
|
|
11247
|
+
"span",
|
|
11248
|
+
{
|
|
11249
|
+
className: `mx-meet__recording mx-meet__recording--${state}`,
|
|
11250
|
+
role: "status",
|
|
11251
|
+
"aria-live": "polite",
|
|
11252
|
+
children: [
|
|
11253
|
+
/* @__PURE__ */ jsx6(RecordIcon, { width: 10, height: 10 }),
|
|
11254
|
+
label
|
|
11255
|
+
]
|
|
11256
|
+
}
|
|
11257
|
+
) : null,
|
|
11258
|
+
stoppedAt !== null ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__recording", role: "status", "data-meet-notice": "recording-stopped", children: "Recording stopped" }) : null,
|
|
11259
|
+
state === "processing" ? (
|
|
11260
|
+
// rec-processing: the host is told it is being made ready (the indicator says Processing…).
|
|
11261
|
+
/* @__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." })
|
|
11262
|
+
) : null,
|
|
11263
|
+
state === "available" && canStart && meetingId !== null ? /* @__PURE__ */ jsx6("a", { className: "mx-meet__link", href: `/meetings/${meetingId}`, target: "_blank", rel: "noreferrer", children: "View recording" }) : null
|
|
11264
|
+
] });
|
|
10940
11265
|
}
|
|
10941
11266
|
function RoleBadge({
|
|
10942
11267
|
role,
|
|
@@ -11088,6 +11413,7 @@ function ControlBar({ onLeave }) {
|
|
|
11088
11413
|
const controls = useHostControls();
|
|
11089
11414
|
const queue = useHandQueue();
|
|
11090
11415
|
const micUnavailable = useLocalMedia().microphone.unavailable;
|
|
11416
|
+
const live = useLivePolicy();
|
|
11091
11417
|
const unread = useUnreadChat(panel === "chat");
|
|
11092
11418
|
const [whiteboard, setWhiteboard] = useState4(false);
|
|
11093
11419
|
const app = useMeetAppPanel();
|
|
@@ -11155,7 +11481,7 @@ function ControlBar({ onLeave }) {
|
|
|
11155
11481
|
panel !== "people" ? /* @__PURE__ */ jsx6(KnockNotice, { onView: () => setPanel("people") }) : null,
|
|
11156
11482
|
controls.notice !== null && panel !== "people" ? /* @__PURE__ */ jsx6(Toast, { text: controls.notice.text, failed: controls.notice.failed, onDone: controls.clearNotice }) : null,
|
|
11157
11483
|
/* @__PURE__ */ jsxs3("div", { className: "mx-meet__controls", children: [
|
|
11158
|
-
/* @__PURE__ */ jsx6(
|
|
11484
|
+
live.micLocked ? null : /* @__PURE__ */ jsx6(
|
|
11159
11485
|
ControlButton,
|
|
11160
11486
|
{
|
|
11161
11487
|
label: micUnavailable !== null ? `Unmute \u2014 ${micUnavailable.remedy}` : room.local?.micEnabled === true ? "Mute (\u2318D)" : "Unmute (\u2318D)",
|
|
@@ -11164,7 +11490,7 @@ function ControlBar({ onLeave }) {
|
|
|
11164
11490
|
children: room.local?.micEnabled === true ? /* @__PURE__ */ jsx6(MicIcon, {}) : /* @__PURE__ */ jsx6(MicOffIcon, {})
|
|
11165
11491
|
}
|
|
11166
11492
|
),
|
|
11167
|
-
/* @__PURE__ */ jsx6(
|
|
11493
|
+
live.cameraLocked ? null : /* @__PURE__ */ jsx6(
|
|
11168
11494
|
ControlButton,
|
|
11169
11495
|
{
|
|
11170
11496
|
label: room.local?.cameraEnabled === true ? "Stop video (\u2318E)" : "Start video (\u2318E)",
|
|
@@ -11173,6 +11499,7 @@ function ControlBar({ onLeave }) {
|
|
|
11173
11499
|
children: room.local?.cameraEnabled === true ? /* @__PURE__ */ jsx6(CameraIcon, {}) : /* @__PURE__ */ jsx6(CameraOffIcon, {})
|
|
11174
11500
|
}
|
|
11175
11501
|
),
|
|
11502
|
+
/* @__PURE__ */ jsx6(CaptionsControl, {}),
|
|
11176
11503
|
room.canShareScreen || room.local?.screenSharing === true ? /* @__PURE__ */ jsx6(
|
|
11177
11504
|
ControlButton,
|
|
11178
11505
|
{
|
|
@@ -11237,6 +11564,7 @@ function ControlBar({ onLeave }) {
|
|
|
11237
11564
|
onOpen: openActivity
|
|
11238
11565
|
}
|
|
11239
11566
|
),
|
|
11567
|
+
/* @__PURE__ */ jsx6(TakeNotesControl, {}),
|
|
11240
11568
|
/* @__PURE__ */ jsx6(AiControl, { active: panel === "ai", onToggle: () => toggle("ai") }),
|
|
11241
11569
|
recording.canStart ? /* @__PURE__ */ jsx6(ControlButton, { label: "Start recording", onClick: () => void recording.start(), children: /* @__PURE__ */ jsx6(RecordIcon, {}) }) : null,
|
|
11242
11570
|
recording.canStop ? /* @__PURE__ */ jsx6(ControlButton, { label: "Stop recording", active: true, onClick: () => void recording.stop(), children: /* @__PURE__ */ jsx6(RecordIcon, {}) }) : null,
|
|
@@ -11292,6 +11620,23 @@ function ControlBar({ onLeave }) {
|
|
|
11292
11620
|
]
|
|
11293
11621
|
}
|
|
11294
11622
|
),
|
|
11623
|
+
/* @__PURE__ */ jsxs3(
|
|
11624
|
+
MenuItem,
|
|
11625
|
+
{
|
|
11626
|
+
onSelect: () => {
|
|
11627
|
+
setPopover("none");
|
|
11628
|
+
void controls.run({
|
|
11629
|
+
action: "set_av_lock",
|
|
11630
|
+
value: snapshot?.livePolicy.avLock === "both" ? "off" : "both"
|
|
11631
|
+
});
|
|
11632
|
+
},
|
|
11633
|
+
children: [
|
|
11634
|
+
/* @__PURE__ */ jsx6(MicOffIcon, { width: 16, height: 16 }),
|
|
11635
|
+
" ",
|
|
11636
|
+
snapshot?.livePolicy.avLock === "both" ? "Turn on attendees' microphone and camera" : "Turn off attendees' microphone and camera"
|
|
11637
|
+
]
|
|
11638
|
+
}
|
|
11639
|
+
),
|
|
11295
11640
|
/* @__PURE__ */ jsxs3(
|
|
11296
11641
|
MenuItem,
|
|
11297
11642
|
{
|
|
@@ -11619,9 +11964,45 @@ function MeetingOverlays() {
|
|
|
11619
11964
|
const reactions = useReactions();
|
|
11620
11965
|
const { notice, dismiss } = useModerationNotice();
|
|
11621
11966
|
const room = useRoom();
|
|
11967
|
+
const live = useLivePolicy();
|
|
11968
|
+
const snapshot = useMeetSnapshot();
|
|
11969
|
+
const controls = useHostControls();
|
|
11970
|
+
const isHostNow = useRole() === "host";
|
|
11622
11971
|
return /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
11623
11972
|
/* @__PURE__ */ jsx6(ReactionOverlay, { reactions }),
|
|
11624
11973
|
/* @__PURE__ */ jsx6(MediaNotices, {}),
|
|
11974
|
+
snapshot !== null && snapshot.phase === "in_call" && snapshot.participants.length >= 2 && !snapshot.participants.some((p) => p.role === "host" || p.role === "cohost") ? (
|
|
11975
|
+
// meeting-without-host: the call goes on; host-only controls are simply not on screen.
|
|
11976
|
+
/* @__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." })
|
|
11977
|
+
) : null,
|
|
11978
|
+
live.consent !== null ? /* @__PURE__ */ jsx6("div", { className: "mx-meet__sheet-backdrop", children: /* @__PURE__ */ jsxs3("div", { className: "mx-meet__sheet", role: "alertdialog", "aria-label": "This meeting is being recorded", "data-meet-notice": "recording-consent", children: [
|
|
11979
|
+
/* @__PURE__ */ jsx6("h2", { className: "mx-meet__panel-title", children: "This meeting is being recorded" }),
|
|
11980
|
+
/* @__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." }),
|
|
11981
|
+
/* @__PURE__ */ jsxs3("div", { className: "mx-meet__menu-actions", children: [
|
|
11982
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__send", onClick: live.agree, children: live.consent === "ok_or_leave" ? "OK" : "I agree" }),
|
|
11983
|
+
live.consent === "ok_or_leave" ? /* @__PURE__ */ jsx6("button", { type: "button", className: "mx-meet__link", onClick: () => void room.leave(), children: "Leave meeting" }) : null
|
|
11984
|
+
] })
|
|
11985
|
+
] }) }) : null,
|
|
11986
|
+
snapshot?.canReclaim === true && !isHostNow ? /* @__PURE__ */ jsxs3("div", { className: "mx-meet__notice-bar", role: "status", "data-meet-notice": "host-reclaim", children: [
|
|
11987
|
+
/* @__PURE__ */ jsx6("span", { children: "You started this meeting. Take back the host?" }),
|
|
11988
|
+
/* @__PURE__ */ jsx6(
|
|
11989
|
+
"button",
|
|
11990
|
+
{
|
|
11991
|
+
type: "button",
|
|
11992
|
+
className: "mx-meet__primary mx-meet__primary--small",
|
|
11993
|
+
onClick: () => void controls.run({ action: "reclaim_host" }),
|
|
11994
|
+
children: "Reclaim host"
|
|
11995
|
+
}
|
|
11996
|
+
)
|
|
11997
|
+
] }) : null,
|
|
11998
|
+
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,
|
|
11999
|
+
live.timeWarning && live.minutesLeft !== null && live.minutesLeft > 0 ? /* @__PURE__ */ jsxs3("p", { className: "mx-meet__banner", role: "status", "data-meet-notice": "time-warning", children: [
|
|
12000
|
+
"This meeting will end in ",
|
|
12001
|
+
live.minutesLeft,
|
|
12002
|
+
" ",
|
|
12003
|
+
live.minutesLeft === 1 ? "minute" : "minutes",
|
|
12004
|
+
" (time limit)."
|
|
12005
|
+
] }) : null,
|
|
11625
12006
|
notice !== null && notice.kind !== "removed" ? (
|
|
11626
12007
|
// A host's request ("the host is asking you to unmute"), not an error:
|
|
11627
12008
|
// an assertive status, so it is announced at once without being filed
|
|
@@ -11712,6 +12093,7 @@ function ChatPanel({
|
|
|
11712
12093
|
}) {
|
|
11713
12094
|
const snapshot = useMeetSnapshot();
|
|
11714
12095
|
const room = useRoom();
|
|
12096
|
+
const live = useLivePolicy();
|
|
11715
12097
|
const [draft, setDraft] = useState4("");
|
|
11716
12098
|
const [recipient, setRecipient] = useState4(to);
|
|
11717
12099
|
const listRef = useRef6(null);
|
|
@@ -11736,7 +12118,7 @@ function ChatPanel({
|
|
|
11736
12118
|
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
12119
|
messages.map((message) => /* @__PURE__ */ jsx6(ChatLine, { message, local, people }, message.id))
|
|
11738
12120
|
] }),
|
|
11739
|
-
/* @__PURE__ */ jsxs3(
|
|
12121
|
+
!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__ */ jsxs3(
|
|
11740
12122
|
"form",
|
|
11741
12123
|
{
|
|
11742
12124
|
className: "mx-meet__composer mx-meet__composer--chat",
|
|
@@ -12108,6 +12490,13 @@ function PersonMenu({
|
|
|
12108
12490
|
/* @__PURE__ */ jsx6(PeopleIcon, { width: 16, height: 16 }),
|
|
12109
12491
|
" Make co-host"
|
|
12110
12492
|
] }) : null,
|
|
12493
|
+
!participant.isAgent && participant.role !== "cohost" && participant.role !== "host" ? participant.role === "presenter" ? /* @__PURE__ */ jsxs3(MenuItem, { onSelect: () => act({ action: "revoke_presenter", identity: participant.identity }), children: [
|
|
12494
|
+
/* @__PURE__ */ jsx6(ScreenShareIcon, { width: 16, height: 16 }),
|
|
12495
|
+
" Remove presenter"
|
|
12496
|
+
] }) : /* @__PURE__ */ jsxs3(MenuItem, { onSelect: () => act({ action: "make_presenter", identity: participant.identity }), children: [
|
|
12497
|
+
/* @__PURE__ */ jsx6(ScreenShareIcon, { width: 16, height: 16 }),
|
|
12498
|
+
" Make presenter"
|
|
12499
|
+
] }) : null,
|
|
12111
12500
|
!participant.isAgent ? /* @__PURE__ */ jsxs3(
|
|
12112
12501
|
MenuItem,
|
|
12113
12502
|
{
|
|
@@ -12613,8 +13002,19 @@ function Captions({ lines = 3 }) {
|
|
|
12613
13002
|
const ai = useMeetAi();
|
|
12614
13003
|
const visible = ai.transcript.slice(-lines);
|
|
12615
13004
|
const translation = useCaptionTranslation(visible);
|
|
12616
|
-
|
|
12617
|
-
|
|
13005
|
+
const captions = useCaptions();
|
|
13006
|
+
if (!captions.on) return null;
|
|
13007
|
+
if (captions.state === "unavailable") {
|
|
13008
|
+
return /* @__PURE__ */ jsx6("div", { className: "mx-meet__captions", role: "status", "data-meet-notice": "captions-unavailable", children: /* @__PURE__ */ jsxs3("p", { className: "mx-meet__caption", children: [
|
|
13009
|
+
"Captions are unavailable. ",
|
|
13010
|
+
captions.reason,
|
|
13011
|
+
captions.remedy !== null ? ` ${captions.remedy}` : ""
|
|
13012
|
+
] }) });
|
|
13013
|
+
}
|
|
13014
|
+
if (visible.length === 0) {
|
|
13015
|
+
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." }) });
|
|
13016
|
+
}
|
|
13017
|
+
return /* @__PURE__ */ jsxs3("div", { className: "mx-meet__captions", "aria-live": "polite", "aria-label": "Live captions", "data-meet-caption": "live", children: [
|
|
12618
13018
|
visible.map((line) => {
|
|
12619
13019
|
const translated = translation.language === null ? void 0 : translation.translations[line.id];
|
|
12620
13020
|
return /* @__PURE__ */ jsxs3(
|
|
@@ -12637,7 +13037,7 @@ function Captions({ lines = 3 }) {
|
|
|
12637
13037
|
function ConsentNotice() {
|
|
12638
13038
|
const notice = useConsentNotice();
|
|
12639
13039
|
if (notice === null) return null;
|
|
12640
|
-
return /* @__PURE__ */ jsx6("p", { className: "mx-meet__consent", role: "status", children: notice });
|
|
13040
|
+
return /* @__PURE__ */ jsx6("p", { className: "mx-meet__consent", role: "status", "data-meet-notice": "notes-started", children: notice });
|
|
12641
13041
|
}
|
|
12642
13042
|
function AttendanceNotice() {
|
|
12643
13043
|
const media = useLocalMedia();
|
|
@@ -14104,6 +14504,7 @@ export {
|
|
|
14104
14504
|
autoAssign,
|
|
14105
14505
|
autoRejoinDelayMs,
|
|
14106
14506
|
availableActivities,
|
|
14507
|
+
breakoutClosingMs,
|
|
14107
14508
|
breakoutRemainingMs,
|
|
14108
14509
|
callInviteAction,
|
|
14109
14510
|
callInviteText,
|
|
@@ -14271,6 +14672,7 @@ export {
|
|
|
14271
14672
|
useCanModerate,
|
|
14272
14673
|
useCanRecord,
|
|
14273
14674
|
useCaptionTranslation,
|
|
14675
|
+
useCaptions,
|
|
14274
14676
|
useCollab,
|
|
14275
14677
|
useCollabPanelRequest,
|
|
14276
14678
|
useConsentNotice,
|