@ai-matrx/meet 0.11.41 → 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 +18 -0
- package/dist/{collab-XY6Xj1dG.d.cts → collab-BNJNgXFB.d.cts} +11 -2
- package/dist/{collab-XY6Xj1dG.d.ts → collab-BNJNgXFB.d.ts} +11 -2
- package/dist/{components-4f9Mf8fz.d.cts → components-BDtGzHf4.d.ts} +4 -2
- package/dist/{components-Cwep2NYU.d.ts → components-BxK993iW.d.cts} +4 -2
- package/dist/core.cjs +190 -25
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +58 -11
- package/dist/core.d.ts +58 -11
- package/dist/core.js +190 -25
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +190 -25
- 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 +190 -25
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +469 -66
- 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 +469 -66
- package/dist/react.js.map +1 -1
- package/dist/skins/meet.cjs +326 -48
- 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 +326 -48
- package/dist/skins/meet.js.map +1 -1
- package/dist/styles.css +12 -0
- 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();
|
|
@@ -5041,8 +5195,7 @@ var ROOM_EVENTS = {
|
|
|
5041
5195
|
/**
|
|
5042
5196
|
* A host or co-host acted (Meet wave 2). Payload `{ meeting_id, kind, … }`:
|
|
5043
5197
|
* `muted {source, by}` · `ask-unmute {by}` · `removed {by}` — addressed to the
|
|
5044
|
-
* person concerned; `
|
|
5045
|
-
* role}` — to everyone.
|
|
5198
|
+
* person concerned; `role {identity, role}` — to everyone.
|
|
5046
5199
|
*/
|
|
5047
5200
|
moderation: "matrx.meet.moderation",
|
|
5048
5201
|
/**
|
|
@@ -5230,10 +5383,11 @@ function moderationText(kind, payload) {
|
|
|
5230
5383
|
switch (kind) {
|
|
5231
5384
|
case "muted": {
|
|
5232
5385
|
const source = text2(payload["source"]) ?? "microphone";
|
|
5233
|
-
|
|
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}.`;
|
|
5234
5388
|
}
|
|
5235
5389
|
case "ask-unmute":
|
|
5236
|
-
return `${by}
|
|
5390
|
+
return `${by === "The host" ? "The host" : by} asked you to unmute.`;
|
|
5237
5391
|
case "removed":
|
|
5238
5392
|
return `${by} removed you from this meeting.`;
|
|
5239
5393
|
default:
|
|
@@ -5259,7 +5413,7 @@ function applyModeration(store, payload, now) {
|
|
|
5259
5413
|
grants: { ...snapshot.grants, role },
|
|
5260
5414
|
moderationNotice: {
|
|
5261
5415
|
kind: "role",
|
|
5262
|
-
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.",
|
|
5263
5417
|
at: now()
|
|
5264
5418
|
}
|
|
5265
5419
|
});
|
|
@@ -5271,7 +5425,10 @@ function applyModeration(store, payload, now) {
|
|
|
5271
5425
|
case "removed": {
|
|
5272
5426
|
const sentence = moderationText(kind, payload);
|
|
5273
5427
|
if (sentence === null) return ignored("a moderation event with no sentence");
|
|
5274
|
-
store.patch({
|
|
5428
|
+
store.patch({
|
|
5429
|
+
moderationNotice: { kind, text: sentence, at: now() },
|
|
5430
|
+
...kind === "muted" && (text2(payload["source"]) ?? "microphone") === "microphone" ? { mutedByHost: true } : {}
|
|
5431
|
+
});
|
|
5275
5432
|
return HANDLED;
|
|
5276
5433
|
}
|
|
5277
5434
|
default:
|
|
@@ -5310,7 +5467,7 @@ function exactNoteTakerParticipant(meeting, participants) {
|
|
|
5310
5467
|
}
|
|
5311
5468
|
function noteTakerStatus(args) {
|
|
5312
5469
|
const present = exactNoteTakerParticipant(args.meeting, args.participants) !== null;
|
|
5313
|
-
const raw = args.meeting?.metadata[NOTE_TAKER_METADATA_KEY];
|
|
5470
|
+
const raw = args.meeting?.metadata?.[NOTE_TAKER_METADATA_KEY];
|
|
5314
5471
|
const row = typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : {};
|
|
5315
5472
|
const recorded = readState(row["state"]);
|
|
5316
5473
|
const detail = typeof row["reason"] === "string" && row["reason"].length > 0 ? row["reason"] : null;
|
|
@@ -5787,6 +5944,7 @@ function createRoomEngine(options) {
|
|
|
5787
5944
|
lastServerUrl = token.serverUrl;
|
|
5788
5945
|
connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;
|
|
5789
5946
|
if (token.leavePolicy !== void 0) store.patch({ leavePolicy: token.leavePolicy });
|
|
5947
|
+
store.patch({ canReclaim: token.canReclaim === true });
|
|
5790
5948
|
store.patch({
|
|
5791
5949
|
localIdentity: token.identity,
|
|
5792
5950
|
breakoutRoomId: token.breakoutRoomId ?? null,
|
|
@@ -6281,13 +6439,17 @@ var MODERATION_ACTIONS = [
|
|
|
6281
6439
|
"ask_unmute",
|
|
6282
6440
|
"ask_all_unmute",
|
|
6283
6441
|
"spotlight",
|
|
6284
|
-
"
|
|
6442
|
+
"sync_presenter",
|
|
6285
6443
|
"raise_hand",
|
|
6286
6444
|
"lower_hand",
|
|
6287
6445
|
"lower_all_hands",
|
|
6288
6446
|
"make_cohost",
|
|
6447
|
+
"make_presenter",
|
|
6448
|
+
"revoke_presenter",
|
|
6449
|
+
"set_av_lock",
|
|
6289
6450
|
"revoke_cohost",
|
|
6290
6451
|
"transfer_host",
|
|
6452
|
+
"reclaim_host",
|
|
6291
6453
|
"rename",
|
|
6292
6454
|
"remove",
|
|
6293
6455
|
"report"
|
|
@@ -6313,18 +6475,17 @@ function createInRoomClient(options) {
|
|
|
6313
6475
|
function refusal2(response, operation, remedy) {
|
|
6314
6476
|
return refusalFromResponse(response, operation, remedy);
|
|
6315
6477
|
}
|
|
6316
|
-
let
|
|
6478
|
+
let toldSharing = false;
|
|
6317
6479
|
let tellChain = Promise.resolve();
|
|
6318
6480
|
store.subscribe(() => {
|
|
6319
6481
|
const local = store.snapshot().participants.find((p) => p.isLocal);
|
|
6320
6482
|
const sharing = local?.screenSharing === true;
|
|
6321
|
-
if (sharing ===
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
tellChain = tellChain.then(() => client.moderate({ action: "present", on })).then(
|
|
6483
|
+
if (sharing === toldSharing || local === void 0) return;
|
|
6484
|
+
toldSharing = sharing;
|
|
6485
|
+
tellChain = tellChain.then(() => client.moderate({ action: "sync_presenter" })).then(
|
|
6325
6486
|
() => void 0,
|
|
6326
6487
|
(error) => {
|
|
6327
|
-
console.warn(`[meet] the server did not take
|
|
6488
|
+
console.warn(`[meet] the server did not take the screen-share change: ${String(error)}`);
|
|
6328
6489
|
}
|
|
6329
6490
|
);
|
|
6330
6491
|
});
|
|
@@ -6341,8 +6502,7 @@ function createInRoomClient(options) {
|
|
|
6341
6502
|
action: request.action,
|
|
6342
6503
|
...request.identity != null ? { identity: request.identity } : {},
|
|
6343
6504
|
...request.name !== void 0 ? { name: request.name } : {},
|
|
6344
|
-
...request.reason !== void 0 ? { reason: request.reason } : {}
|
|
6345
|
-
...request.on !== void 0 ? { on: request.on } : {}
|
|
6505
|
+
...request.reason !== void 0 ? { reason: request.reason } : {}
|
|
6346
6506
|
},
|
|
6347
6507
|
...hdrs !== void 0 ? { headers: hdrs } : {}
|
|
6348
6508
|
});
|
|
@@ -7327,11 +7487,13 @@ import { useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2 } from
|
|
|
7327
7487
|
|
|
7328
7488
|
// src/core/observation.ts
|
|
7329
7489
|
function observedRole(role, standing) {
|
|
7330
|
-
if (role === "host" || role === "cohost") return role;
|
|
7490
|
+
if (role === "host" || role === "cohost" || role === "presenter") return role;
|
|
7331
7491
|
return isLinkFollower(standing) ? "guest" : "participant";
|
|
7332
7492
|
}
|
|
7333
|
-
function observedDevice(track, permission = "unknown") {
|
|
7493
|
+
function observedDevice(track, permission = "unknown", held = null) {
|
|
7494
|
+
if (held === "locked") return "locked";
|
|
7334
7495
|
if (track.enabled) return "on";
|
|
7496
|
+
if (held === "muted_by_host") return "muted_by_host";
|
|
7335
7497
|
const reason = deviceUnavailableReason(track, permission);
|
|
7336
7498
|
return reason === null ? "muted_by_me" : `unavailable:${reason}`;
|
|
7337
7499
|
}
|
|
@@ -7368,10 +7530,20 @@ function observe(input) {
|
|
|
7368
7530
|
recording: snapshot?.recording === "recording" || snapshot?.recording === "stopping",
|
|
7369
7531
|
// S6c: the autoplay region is known only inside the room (no remote audio exists elsewhere).
|
|
7370
7532
|
audioBlocked: inCall && snapshot !== null ? snapshot.mediaRegion.audioOutput === "blocked" : null,
|
|
7371
|
-
camera: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.camera, snapshot.mediaRegion.permission.camera) : null,
|
|
7372
|
-
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
|
|
7373
7535
|
};
|
|
7374
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
|
+
}
|
|
7375
7547
|
function staticObservation(phase, reason = null) {
|
|
7376
7548
|
return {
|
|
7377
7549
|
phase,
|
|
@@ -8561,6 +8733,45 @@ function useMeetAi() {
|
|
|
8561
8733
|
}, [ai, meeting, dispatchNoteTaker])
|
|
8562
8734
|
};
|
|
8563
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
|
+
}
|
|
8564
8775
|
function useEndMeeting() {
|
|
8565
8776
|
const host = useMeetHost();
|
|
8566
8777
|
const snapshot = useMeetSnapshot();
|
|
@@ -8598,7 +8809,7 @@ function useConsentNotice() {
|
|
|
8598
8809
|
const snapshot = useMeetSnapshot();
|
|
8599
8810
|
const agent = noteTakerParticipant(snapshot?.participants ?? []);
|
|
8600
8811
|
if (agent === null) return null;
|
|
8601
|
-
return
|
|
8812
|
+
return `AI notes are on. ${agent.displayName} is in this meeting, transcribing what is said and writing notes everyone here can read.`;
|
|
8602
8813
|
}
|
|
8603
8814
|
function useMediaSupervisor() {
|
|
8604
8815
|
const host = useMeetHost();
|
|
@@ -9062,6 +9273,38 @@ function useGuestRecordAccess(meeting) {
|
|
|
9062
9273
|
)
|
|
9063
9274
|
};
|
|
9064
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
|
+
}
|
|
9065
9308
|
|
|
9066
9309
|
// src/react/skin-root.tsx
|
|
9067
9310
|
import { useEffect as useEffect8 } from "react";
|
|
@@ -9836,6 +10079,18 @@ function SparklesIcon(props) {
|
|
|
9836
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" })
|
|
9837
10080
|
] });
|
|
9838
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
|
+
}
|
|
9839
10094
|
function LockIcon(props) {
|
|
9840
10095
|
return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
|
|
9841
10096
|
/* @__PURE__ */ jsx4("rect", { x: "4", y: "10", width: "16", height: "11", rx: "2" }),
|
|
@@ -10742,7 +10997,7 @@ function BreakoutRun({ plan }) {
|
|
|
10742
10997
|
type: "button",
|
|
10743
10998
|
className: "mx-meet__primary mx-meet__primary--small",
|
|
10744
10999
|
onClick: () => void act({ action: "breakout_close" }).then(show),
|
|
10745
|
-
children: "
|
|
11000
|
+
children: "Close rooms"
|
|
10746
11001
|
}
|
|
10747
11002
|
) : null
|
|
10748
11003
|
] });
|
|
@@ -10760,9 +11015,14 @@ function BreakoutBanner() {
|
|
|
10760
11015
|
const { collab, act, moveTo, localIdentity, breakoutRoomId } = useCollab();
|
|
10761
11016
|
const plan = collab?.breakout ?? null;
|
|
10762
11017
|
const open = plan?.state === "open" ? plan : null;
|
|
10763
|
-
const now = useNow(open?.endsAt != null);
|
|
11018
|
+
const now = useNow(open?.endsAt != null || open?.closingAt != null);
|
|
10764
11019
|
const remaining = breakoutRemainingMs(open, now);
|
|
11020
|
+
const closing = breakoutClosingMs(open, now);
|
|
10765
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;
|
|
10766
11026
|
const [dismissedBroadcast, setDismissedBroadcast] = useState3(null);
|
|
10767
11027
|
const [failure, setFailure] = useState3(null);
|
|
10768
11028
|
const expireAsked = useRef5(null);
|
|
@@ -10775,6 +11035,19 @@ function BreakoutBanner() {
|
|
|
10775
11035
|
if ((here ?? null) === request.roomId) return;
|
|
10776
11036
|
void moveTo(request.roomId).then((result) => setFailure(result.ok ? null : result.message));
|
|
10777
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]);
|
|
10778
11051
|
useEffect5(() => {
|
|
10779
11052
|
if (open === null || remaining === null || remaining > 0) return;
|
|
10780
11053
|
const asked = expireAsked.current;
|
|
@@ -10788,27 +11061,25 @@ function BreakoutBanner() {
|
|
|
10788
11061
|
if (open === null && failure === null) return null;
|
|
10789
11062
|
return /* @__PURE__ */ jsxs2("div", { className: "mx-meet__breakout-banner", role: "status", children: [
|
|
10790
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,
|
|
10791
11069
|
room !== null ? /* @__PURE__ */ jsxs2("span", { children: [
|
|
10792
11070
|
/* @__PURE__ */ jsx5("strong", { children: room.name }),
|
|
10793
|
-
remaining !== null ? ` \xB7 ${clock(remaining)} left` : "",
|
|
11071
|
+
remaining !== null && closing === null ? ` \xB7 ${clock(remaining)} left` : "",
|
|
10794
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" })
|
|
10795
11078
|
] }) : assigned !== null ? /* @__PURE__ */ jsxs2("span", { children: [
|
|
10796
|
-
|
|
10797
|
-
|
|
10798
|
-
|
|
10799
|
-
|
|
10800
|
-
"button",
|
|
10801
|
-
{
|
|
10802
|
-
type: "button",
|
|
10803
|
-
className: "mx-meet__primary mx-meet__primary--small",
|
|
10804
|
-
onClick: () => void act({ action: "breakout_join", roomId: assigned.id }).then(async (said) => {
|
|
10805
|
-
const result = said.ok ? await moveTo(assigned.id) : said;
|
|
10806
|
-
setFailure(result.ok ? null : result.message);
|
|
10807
|
-
}),
|
|
10808
|
-
children: "Join"
|
|
10809
|
-
}
|
|
10810
|
-
)
|
|
10811
|
-
] }) : 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,
|
|
10812
11083
|
showBroadcast ? /* @__PURE__ */ jsxs2("span", { className: "mx-meet__breakout-broadcast", children: [
|
|
10813
11084
|
/* @__PURE__ */ jsxs2("strong", { children: [
|
|
10814
11085
|
broadcast.by,
|
|
@@ -10856,6 +11127,33 @@ function CaptionLanguageSettings() {
|
|
|
10856
11127
|
collab?.captionTranslationError != null ? /* @__PURE__ */ jsx5("p", { className: "mx-meet__panel-note mx-meet__collab-error", children: collab.captionTranslationError }) : null
|
|
10857
11128
|
] });
|
|
10858
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
|
+
}
|
|
10859
11157
|
|
|
10860
11158
|
// src/skins/meet/components.tsx
|
|
10861
11159
|
import { Fragment, jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -10906,6 +11204,7 @@ function ControlButton({
|
|
|
10906
11204
|
label,
|
|
10907
11205
|
active = false,
|
|
10908
11206
|
danger = false,
|
|
11207
|
+
pressed,
|
|
10909
11208
|
onClick,
|
|
10910
11209
|
children
|
|
10911
11210
|
}) {
|
|
@@ -10920,26 +11219,49 @@ function ControlButton({
|
|
|
10920
11219
|
].filter(Boolean).join(" "),
|
|
10921
11220
|
"aria-label": label,
|
|
10922
11221
|
title: label,
|
|
11222
|
+
...pressed !== void 0 ? { "aria-pressed": pressed } : {},
|
|
10923
11223
|
onClick,
|
|
10924
11224
|
children
|
|
10925
11225
|
}
|
|
10926
11226
|
);
|
|
10927
11227
|
}
|
|
10928
11228
|
function RecordingIndicator() {
|
|
10929
|
-
const { state, label } = useRecording();
|
|
10930
|
-
|
|
10931
|
-
|
|
10932
|
-
|
|
10933
|
-
|
|
10934
|
-
|
|
10935
|
-
|
|
10936
|
-
|
|
10937
|
-
|
|
10938
|
-
|
|
10939
|
-
|
|
10940
|
-
|
|
10941
|
-
|
|
10942
|
-
);
|
|
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
|
+
] });
|
|
10943
11265
|
}
|
|
10944
11266
|
function RoleBadge({
|
|
10945
11267
|
role,
|
|
@@ -10966,6 +11288,7 @@ function ParticipantTile({
|
|
|
10966
11288
|
const showVideo = participant.cameraEnabled || participant.screenSharing;
|
|
10967
11289
|
const pinnedHere = snapshot?.pinned === participant.identity;
|
|
10968
11290
|
const spotlit = snapshot?.spotlight === participant.identity;
|
|
11291
|
+
const presenting = snapshot?.presenter === participant.identity;
|
|
10969
11292
|
const handPosition = participant.handRaised ? queue.findIndex((entry) => entry.identity === participant.identity) + 1 : 0;
|
|
10970
11293
|
return /* @__PURE__ */ jsxs3(
|
|
10971
11294
|
"div",
|
|
@@ -10977,6 +11300,7 @@ function ParticipantTile({
|
|
|
10977
11300
|
].filter(Boolean).join(" "),
|
|
10978
11301
|
"data-identity": participant.identity,
|
|
10979
11302
|
...participantAttributes(participant),
|
|
11303
|
+
...presenting ? { "data-meet-participant-presenting": "true" } : {},
|
|
10980
11304
|
children: [
|
|
10981
11305
|
showVideo ? /* @__PURE__ */ jsx6(
|
|
10982
11306
|
"video",
|
|
@@ -10988,6 +11312,7 @@ function ParticipantTile({
|
|
|
10988
11312
|
muted: participant.isLocal
|
|
10989
11313
|
}
|
|
10990
11314
|
) : /* @__PURE__ */ jsx6("div", { className: "mx-meet__tile-placeholder", children: /* @__PURE__ */ jsx6(Avatar, { name: participant.displayName, url: participant.avatarUrl, size: featured ? 96 : 56 }) }),
|
|
11315
|
+
presenting ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__tile-presenting", role: "status", children: participant.isLocal ? "You are presenting" : `${participant.displayName} is presenting` }) : null,
|
|
10991
11316
|
participant.handRaised ? /* @__PURE__ */ jsxs3("span", { className: "mx-meet__tile-hand", title: `Hand raised \xB7 ${ordinal(handPosition)} in line`, children: [
|
|
10992
11317
|
/* @__PURE__ */ jsx6(HandIcon, { width: 14, height: 14 }),
|
|
10993
11318
|
handPosition > 0 ? handPosition : null
|
|
@@ -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",
|
|
@@ -11919,7 +12301,8 @@ function PeoplePanel({
|
|
|
11919
12301
|
}
|
|
11920
12302
|
),
|
|
11921
12303
|
isNoteTaker(participant) ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__badge", children: "Meeting assistant" }) : null,
|
|
11922
|
-
snapshot?.spotlight === participant.identity ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__badge", children: "Spotlight" }) : null
|
|
12304
|
+
snapshot?.spotlight === participant.identity ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__badge", children: "Spotlight" }) : null,
|
|
12305
|
+
snapshot?.presenter === participant.identity ? /* @__PURE__ */ jsx6("span", { className: "mx-meet__badge", "data-meet-presenting": "true", children: "Presenting" }) : null
|
|
11923
12306
|
] }),
|
|
11924
12307
|
participant.handRaised ? /* @__PURE__ */ jsx6(HandIcon, { width: 14, height: 14 }) : null,
|
|
11925
12308
|
!participant.micEnabled && !isNoteTaker(participant) ? /* @__PURE__ */ jsx6(MicOffIcon, { width: 14, height: 14 }) : null,
|
|
@@ -12107,6 +12490,13 @@ function PersonMenu({
|
|
|
12107
12490
|
/* @__PURE__ */ jsx6(PeopleIcon, { width: 16, height: 16 }),
|
|
12108
12491
|
" Make co-host"
|
|
12109
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,
|
|
12110
12500
|
!participant.isAgent ? /* @__PURE__ */ jsxs3(
|
|
12111
12501
|
MenuItem,
|
|
12112
12502
|
{
|
|
@@ -12612,8 +13002,19 @@ function Captions({ lines = 3 }) {
|
|
|
12612
13002
|
const ai = useMeetAi();
|
|
12613
13003
|
const visible = ai.transcript.slice(-lines);
|
|
12614
13004
|
const translation = useCaptionTranslation(visible);
|
|
12615
|
-
|
|
12616
|
-
|
|
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: [
|
|
12617
13018
|
visible.map((line) => {
|
|
12618
13019
|
const translated = translation.language === null ? void 0 : translation.translations[line.id];
|
|
12619
13020
|
return /* @__PURE__ */ jsxs3(
|
|
@@ -12636,7 +13037,7 @@ function Captions({ lines = 3 }) {
|
|
|
12636
13037
|
function ConsentNotice() {
|
|
12637
13038
|
const notice = useConsentNotice();
|
|
12638
13039
|
if (notice === null) return null;
|
|
12639
|
-
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 });
|
|
12640
13041
|
}
|
|
12641
13042
|
function AttendanceNotice() {
|
|
12642
13043
|
const media = useLocalMedia();
|
|
@@ -14103,6 +14504,7 @@ export {
|
|
|
14103
14504
|
autoAssign,
|
|
14104
14505
|
autoRejoinDelayMs,
|
|
14105
14506
|
availableActivities,
|
|
14507
|
+
breakoutClosingMs,
|
|
14106
14508
|
breakoutRemainingMs,
|
|
14107
14509
|
callInviteAction,
|
|
14108
14510
|
callInviteText,
|
|
@@ -14270,6 +14672,7 @@ export {
|
|
|
14270
14672
|
useCanModerate,
|
|
14271
14673
|
useCanRecord,
|
|
14272
14674
|
useCaptionTranslation,
|
|
14675
|
+
useCaptions,
|
|
14273
14676
|
useCollab,
|
|
14274
14677
|
useCollabPanelRequest,
|
|
14275
14678
|
useConsentNotice,
|