@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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog — `@ai-matrx/meet`
|
|
2
2
|
|
|
3
|
+
## 0.11.43
|
|
4
|
+
|
|
5
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
6
|
+
`git diff npm/meet/v0.11.42..npm/meet/v0.11.43 -- apps/shared/meet`).
|
|
7
|
+
No source changes intended and no consumer action required.
|
|
8
|
+
|
|
3
9
|
## 0.11.42
|
|
4
10
|
|
|
5
11
|
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
@@ -779,6 +779,10 @@ interface MeetBreakoutPlan {
|
|
|
779
779
|
readonly minutes: number;
|
|
780
780
|
readonly openedAt: string | null;
|
|
781
781
|
readonly endsAt: string | null;
|
|
782
|
+
/** Set while the host's closing countdown runs: everyone returns to the main meeting at this time. */
|
|
783
|
+
readonly closingAt: string | null;
|
|
784
|
+
/** `invite` (Join / Later) or `auto` (a countdown, then moved) — the org's `meet.breakout_move`. */
|
|
785
|
+
readonly moveMode: "invite" | "auto";
|
|
782
786
|
readonly closedAt: string | null;
|
|
783
787
|
readonly broadcasts: readonly MeetBreakoutBroadcast[];
|
|
784
788
|
}
|
|
@@ -861,6 +865,11 @@ declare function assignedRoom(plan: MeetBreakoutPlan | null, identity: string |
|
|
|
861
865
|
declare function autoAssign(identities: readonly string[], rooms: number, exclude?: readonly string[]): string[][];
|
|
862
866
|
/** Milliseconds until the plan's timer ends, or null when it has none. */
|
|
863
867
|
declare function breakoutRemainingMs(plan: MeetBreakoutPlan | null, now: number): number | null;
|
|
868
|
+
/**
|
|
869
|
+
* The closing countdown to show, in ms — or null when the rooms are not closing. Closing
|
|
870
|
+
* means the host asked (`closingAt`) or the timer is within its last 30 seconds.
|
|
871
|
+
*/
|
|
872
|
+
declare function breakoutClosingMs(plan: MeetBreakoutPlan | null, now: number): number | null;
|
|
864
873
|
/** The language translated captions start in: the knob, else the viewer's own. */
|
|
865
874
|
declare function defaultCaptionLanguage(settings: MeetCollabSettings | null, viewerLanguage: string | null | undefined): string;
|
|
866
875
|
/** Languages offered in the captions menu (tags the translator understands). */
|
|
@@ -883,4 +892,4 @@ interface MeetingActivities {
|
|
|
883
892
|
*/
|
|
884
893
|
declare function loadMeetingActivities(api: MeetApi, meetingId: string, meetingPass?: string | null): Promise<MeetingActivities>;
|
|
885
894
|
|
|
886
|
-
export { type MeetingOccurrence as $, type AccessTokenSource as A, type MeetPoll as B, type CollabPanel as C, type MeetPollOption as D, EMPTY_COLLAB as E, FILE_SESSION_PATH as F, type MeetPollState as G, type MeetQuestion as H, type InviteeInput as I, type JsonObject as J, type MeetQuestionState as K, type LobbyEntry as L, type MeetingRecord as M, type MeetStroke as N, type OrganizationId as O, type ParticipantIdentity as P, type MeetingActivities as Q, type RoomName as R, type StageLayout as S, type TranscriptSource as T, type UserId as U, type MeetingAttendee as V, type WrapUpAnnouncement as W, type MeetingChanges as X, type MeetingInvitee as Y, type MeetingKind as Z, type MeetingNoteKind as _, type MeetParticipant as a, type MeetingSettings as a0, type NoteTakerState as a1, ORGANIZATION_HEADER as a2, type ObserversVisibleTo as a3, type OccurrenceOverride as a4, type OccurrenceState as a5, type ParticipantStanding as a6, ROLE_RANK as a7, type RecordingPolicy as a8, type RecordingRecord as a9,
|
|
895
|
+
export { type MeetingOccurrence as $, type AccessTokenSource as A, type MeetPoll as B, type CollabPanel as C, type MeetPollOption as D, EMPTY_COLLAB as E, FILE_SESSION_PATH as F, type MeetPollState as G, type MeetQuestion as H, type InviteeInput as I, type JsonObject as J, type MeetQuestionState as K, type LobbyEntry as L, type MeetingRecord as M, type MeetStroke as N, type OrganizationId as O, type ParticipantIdentity as P, type MeetingActivities as Q, type RoomName as R, type StageLayout as S, type TranscriptSource as T, type UserId as U, type MeetingAttendee as V, type WrapUpAnnouncement as W, type MeetingChanges as X, type MeetingInvitee as Y, type MeetingKind as Z, type MeetingNoteKind as _, type MeetParticipant as a, type MeetingSettings as a0, type NoteTakerState as a1, ORGANIZATION_HEADER as a2, type ObserversVisibleTo as a3, type OccurrenceOverride as a4, type OccurrenceState as a5, type ParticipantStanding as a6, ROLE_RANK as a7, type RecordingPolicy as a8, type RecordingRecord as a9, isLinkFollower as aA, isObserverRole as aB, loadMeetingActivities as aC, scheduleSettingsPatch as aD, withItem as aE, type RecordingSource as aF, type RecordingState as aa, type RsvpAnswer as ab, type RsvpInvitation as ac, type RsvpState as ad, type TranscriptSegment as ae, type UpcomingOccurrence as af, appPanelOf as ag, applyCollabPacket as ah, asCallId as ai, asMeetingId as aj, asOrganizationId as ak, asParticipantIdentity as al, asRoomName as am, asUserId as an, assignedRoom as ao, autoAssign as ap, breakoutClosingMs as aq, breakoutRemainingMs as ar, captureHeldOf as as, collabFromState as at, createMeetApi as au, defaultCaptionLanguage as av, fileDownloadPath as aw, focusFromRow as ax, isGuest as ay, isHostRole as az, type MeetApi as b, type MeetIdentity as c, type CallInvite as d, type ParticipantRole as e, type CallMode as f, type MeetingNote as g, type MeetingId as h, type MeetingAppPanel as i, type CollabSnapshot as j, type AdmissionState as k, CAPTION_LANGUAGES as l, type CallId as m, type CallState as n, type CallablePerson as o, type ConnectionQuality as p, type InviteeRole as q, type JsonValue as r, type MeetApiOptions as s, type MeetApiRequest as t, type MeetAppFocus as u, type MeetBreakoutBroadcast as v, type MeetBreakoutPlan as w, type MeetBreakoutRoom as x, type MeetCollabSettings as y, type MeetFileMedia as z };
|
|
@@ -779,6 +779,10 @@ interface MeetBreakoutPlan {
|
|
|
779
779
|
readonly minutes: number;
|
|
780
780
|
readonly openedAt: string | null;
|
|
781
781
|
readonly endsAt: string | null;
|
|
782
|
+
/** Set while the host's closing countdown runs: everyone returns to the main meeting at this time. */
|
|
783
|
+
readonly closingAt: string | null;
|
|
784
|
+
/** `invite` (Join / Later) or `auto` (a countdown, then moved) — the org's `meet.breakout_move`. */
|
|
785
|
+
readonly moveMode: "invite" | "auto";
|
|
782
786
|
readonly closedAt: string | null;
|
|
783
787
|
readonly broadcasts: readonly MeetBreakoutBroadcast[];
|
|
784
788
|
}
|
|
@@ -861,6 +865,11 @@ declare function assignedRoom(plan: MeetBreakoutPlan | null, identity: string |
|
|
|
861
865
|
declare function autoAssign(identities: readonly string[], rooms: number, exclude?: readonly string[]): string[][];
|
|
862
866
|
/** Milliseconds until the plan's timer ends, or null when it has none. */
|
|
863
867
|
declare function breakoutRemainingMs(plan: MeetBreakoutPlan | null, now: number): number | null;
|
|
868
|
+
/**
|
|
869
|
+
* The closing countdown to show, in ms — or null when the rooms are not closing. Closing
|
|
870
|
+
* means the host asked (`closingAt`) or the timer is within its last 30 seconds.
|
|
871
|
+
*/
|
|
872
|
+
declare function breakoutClosingMs(plan: MeetBreakoutPlan | null, now: number): number | null;
|
|
864
873
|
/** The language translated captions start in: the knob, else the viewer's own. */
|
|
865
874
|
declare function defaultCaptionLanguage(settings: MeetCollabSettings | null, viewerLanguage: string | null | undefined): string;
|
|
866
875
|
/** Languages offered in the captions menu (tags the translator understands). */
|
|
@@ -883,4 +892,4 @@ interface MeetingActivities {
|
|
|
883
892
|
*/
|
|
884
893
|
declare function loadMeetingActivities(api: MeetApi, meetingId: string, meetingPass?: string | null): Promise<MeetingActivities>;
|
|
885
894
|
|
|
886
|
-
export { type MeetingOccurrence as $, type AccessTokenSource as A, type MeetPoll as B, type CollabPanel as C, type MeetPollOption as D, EMPTY_COLLAB as E, FILE_SESSION_PATH as F, type MeetPollState as G, type MeetQuestion as H, type InviteeInput as I, type JsonObject as J, type MeetQuestionState as K, type LobbyEntry as L, type MeetingRecord as M, type MeetStroke as N, type OrganizationId as O, type ParticipantIdentity as P, type MeetingActivities as Q, type RoomName as R, type StageLayout as S, type TranscriptSource as T, type UserId as U, type MeetingAttendee as V, type WrapUpAnnouncement as W, type MeetingChanges as X, type MeetingInvitee as Y, type MeetingKind as Z, type MeetingNoteKind as _, type MeetParticipant as a, type MeetingSettings as a0, type NoteTakerState as a1, ORGANIZATION_HEADER as a2, type ObserversVisibleTo as a3, type OccurrenceOverride as a4, type OccurrenceState as a5, type ParticipantStanding as a6, ROLE_RANK as a7, type RecordingPolicy as a8, type RecordingRecord as a9,
|
|
895
|
+
export { type MeetingOccurrence as $, type AccessTokenSource as A, type MeetPoll as B, type CollabPanel as C, type MeetPollOption as D, EMPTY_COLLAB as E, FILE_SESSION_PATH as F, type MeetPollState as G, type MeetQuestion as H, type InviteeInput as I, type JsonObject as J, type MeetQuestionState as K, type LobbyEntry as L, type MeetingRecord as M, type MeetStroke as N, type OrganizationId as O, type ParticipantIdentity as P, type MeetingActivities as Q, type RoomName as R, type StageLayout as S, type TranscriptSource as T, type UserId as U, type MeetingAttendee as V, type WrapUpAnnouncement as W, type MeetingChanges as X, type MeetingInvitee as Y, type MeetingKind as Z, type MeetingNoteKind as _, type MeetParticipant as a, type MeetingSettings as a0, type NoteTakerState as a1, ORGANIZATION_HEADER as a2, type ObserversVisibleTo as a3, type OccurrenceOverride as a4, type OccurrenceState as a5, type ParticipantStanding as a6, ROLE_RANK as a7, type RecordingPolicy as a8, type RecordingRecord as a9, isLinkFollower as aA, isObserverRole as aB, loadMeetingActivities as aC, scheduleSettingsPatch as aD, withItem as aE, type RecordingSource as aF, type RecordingState as aa, type RsvpAnswer as ab, type RsvpInvitation as ac, type RsvpState as ad, type TranscriptSegment as ae, type UpcomingOccurrence as af, appPanelOf as ag, applyCollabPacket as ah, asCallId as ai, asMeetingId as aj, asOrganizationId as ak, asParticipantIdentity as al, asRoomName as am, asUserId as an, assignedRoom as ao, autoAssign as ap, breakoutClosingMs as aq, breakoutRemainingMs as ar, captureHeldOf as as, collabFromState as at, createMeetApi as au, defaultCaptionLanguage as av, fileDownloadPath as aw, focusFromRow as ax, isGuest as ay, isHostRole as az, type MeetApi as b, type MeetIdentity as c, type CallInvite as d, type ParticipantRole as e, type CallMode as f, type MeetingNote as g, type MeetingId as h, type MeetingAppPanel as i, type CollabSnapshot as j, type AdmissionState as k, CAPTION_LANGUAGES as l, type CallId as m, type CallState as n, type CallablePerson as o, type ConnectionQuality as p, type InviteeRole as q, type JsonValue as r, type MeetApiOptions as s, type MeetApiRequest as t, type MeetAppFocus as u, type MeetBreakoutBroadcast as v, type MeetBreakoutPlan as w, type MeetBreakoutRoom as x, type MeetCollabSettings as y, type MeetFileMedia as z };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { M as MeetingRecord, P as ParticipantIdentity, R as RoomName, a as MeetParticipant } from './collab-
|
|
2
|
+
import { M as MeetingRecord, P as ParticipantIdentity, R as RoomName, a as MeetParticipant } from './collab-BNJNgXFB.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* THE PRODUCT UI (R6/R7). Not a headless kernel with a note telling the host to
|
|
@@ -26,10 +26,12 @@ declare function Avatar({ name, url, size, }: {
|
|
|
26
26
|
url?: string | null;
|
|
27
27
|
size?: number;
|
|
28
28
|
}): ReactNode;
|
|
29
|
-
declare function ControlButton({ label, active, danger, onClick, children, }: {
|
|
29
|
+
declare function ControlButton({ label, active, danger, pressed, onClick, children, }: {
|
|
30
30
|
label: string;
|
|
31
31
|
active?: boolean;
|
|
32
32
|
danger?: boolean;
|
|
33
|
+
/** A toggle's state, for assistive tech and the harness (`aria-pressed`). */
|
|
34
|
+
pressed?: boolean | undefined;
|
|
33
35
|
onClick: () => void;
|
|
34
36
|
children: ReactNode;
|
|
35
37
|
}): ReactNode;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { M as MeetingRecord, P as ParticipantIdentity, R as RoomName, a as MeetParticipant } from './collab-
|
|
2
|
+
import { M as MeetingRecord, P as ParticipantIdentity, R as RoomName, a as MeetParticipant } from './collab-BNJNgXFB.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* THE PRODUCT UI (R6/R7). Not a headless kernel with a note telling the host to
|
|
@@ -26,10 +26,12 @@ declare function Avatar({ name, url, size, }: {
|
|
|
26
26
|
url?: string | null;
|
|
27
27
|
size?: number;
|
|
28
28
|
}): ReactNode;
|
|
29
|
-
declare function ControlButton({ label, active, danger, onClick, children, }: {
|
|
29
|
+
declare function ControlButton({ label, active, danger, pressed, onClick, children, }: {
|
|
30
30
|
label: string;
|
|
31
31
|
active?: boolean;
|
|
32
32
|
danger?: boolean;
|
|
33
|
+
/** A toggle's state, for assistive tech and the harness (`aria-pressed`). */
|
|
34
|
+
pressed?: boolean | undefined;
|
|
33
35
|
onClick: () => void;
|
|
34
36
|
children: ReactNode;
|
|
35
37
|
}): ReactNode;
|
package/dist/core.cjs
CHANGED
|
@@ -110,6 +110,7 @@ __export(core_exports, {
|
|
|
110
110
|
autoAssign: () => autoAssign,
|
|
111
111
|
autoRejoinDelayMs: () => autoRejoinDelayMs,
|
|
112
112
|
availableActivities: () => availableActivities,
|
|
113
|
+
breakoutClosingMs: () => breakoutClosingMs,
|
|
113
114
|
breakoutRemainingMs: () => breakoutRemainingMs,
|
|
114
115
|
callInviteAction: () => callInviteAction,
|
|
115
116
|
callInviteText: () => callInviteText,
|
|
@@ -911,6 +912,8 @@ var MEET_ROUTES = {
|
|
|
911
912
|
noteTakerStop: "/api/v1/meet/agent/stop",
|
|
912
913
|
/** In-meeting Q&A. Streams NDJSON; body is `{meeting_id, question}` only. */
|
|
913
914
|
intelligenceAsk: "/api/v1/meet/intelligence/ask",
|
|
915
|
+
/** Anyone in the meeting (guests with the room pass included) asks for captions for themselves. */
|
|
916
|
+
noteTakerCaptions: "/api/v1/meet/agent/captions",
|
|
914
917
|
/** A host ends the meeting for everyone. Served by MRI-A6. */
|
|
915
918
|
meetingEnd: "/api/v1/meet/end",
|
|
916
919
|
/**
|
|
@@ -1057,7 +1060,8 @@ function parseRoomToken(body, roomName) {
|
|
|
1057
1060
|
breakoutRoomId: readString(payload, "breakout_room_id") ?? null,
|
|
1058
1061
|
observersVisibleTo: readString(payload, "observers_visible_to") === "hosts" ? "hosts" : "everyone",
|
|
1059
1062
|
connectionPolicy: connectionPolicyFrom(payload["connection_policy"]),
|
|
1060
|
-
leavePolicy: leavePolicyFrom(payload["leave_policy"])
|
|
1063
|
+
leavePolicy: leavePolicyFrom(payload["leave_policy"]),
|
|
1064
|
+
canReclaim: payload["can_reclaim"] === true
|
|
1061
1065
|
};
|
|
1062
1066
|
}
|
|
1063
1067
|
function createTokenClient(options) {
|
|
@@ -1519,6 +1523,42 @@ function createMeetAi(options) {
|
|
|
1519
1523
|
},
|
|
1520
1524
|
"meet.ai.startNoteTaker"
|
|
1521
1525
|
),
|
|
1526
|
+
requestCaptions: async ({ meetingId }) => {
|
|
1527
|
+
const roomToken = options.resolveRoomToken?.() ?? null;
|
|
1528
|
+
const operation = "meet.ai.requestCaptions";
|
|
1529
|
+
try {
|
|
1530
|
+
const response = await send({
|
|
1531
|
+
path: MEET_ROUTES.noteTakerCaptions,
|
|
1532
|
+
body: { meeting_id: meetingId },
|
|
1533
|
+
operation,
|
|
1534
|
+
requireAuth: roomToken === null,
|
|
1535
|
+
...roomToken !== null ? { headers: { [MEETING_PASS_HEADER]: roomToken } } : {}
|
|
1536
|
+
});
|
|
1537
|
+
if (!response.ok) {
|
|
1538
|
+
options.store.patch({ captionsRefusal: await refusalFor(response, operation) });
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
let payload = null;
|
|
1542
|
+
try {
|
|
1543
|
+
payload = await response.json();
|
|
1544
|
+
} catch {
|
|
1545
|
+
payload = null;
|
|
1546
|
+
}
|
|
1547
|
+
const row = typeof payload === "object" && payload !== null ? payload : {};
|
|
1548
|
+
const detail = typeof row["detail"] === "string" ? row["detail"] : "";
|
|
1549
|
+
options.store.patch({
|
|
1550
|
+
captionsRefusal: row["dispatched"] === true ? null : { status: 200, reason: detail, remedy: "" }
|
|
1551
|
+
});
|
|
1552
|
+
} catch (error) {
|
|
1553
|
+
options.store.patch({
|
|
1554
|
+
captionsRefusal: {
|
|
1555
|
+
status: 0,
|
|
1556
|
+
reason: error.message,
|
|
1557
|
+
remedy: error.remedy ?? ""
|
|
1558
|
+
}
|
|
1559
|
+
});
|
|
1560
|
+
}
|
|
1561
|
+
},
|
|
1522
1562
|
stopNoteTaker: ({ meetingId }) => dispatch(
|
|
1523
1563
|
MEET_ROUTES.noteTakerStop,
|
|
1524
1564
|
{ meeting_id: meetingId },
|
|
@@ -1557,7 +1597,7 @@ function exactNoteTakerParticipant(meeting, participants) {
|
|
|
1557
1597
|
}
|
|
1558
1598
|
function noteTakerStatus(args) {
|
|
1559
1599
|
const present = exactNoteTakerParticipant(args.meeting, args.participants) !== null;
|
|
1560
|
-
const raw = args.meeting?.metadata[NOTE_TAKER_METADATA_KEY];
|
|
1600
|
+
const raw = args.meeting?.metadata?.[NOTE_TAKER_METADATA_KEY];
|
|
1561
1601
|
const row = typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : {};
|
|
1562
1602
|
const recorded = readState(row["state"]);
|
|
1563
1603
|
const detail = typeof row["reason"] === "string" && row["reason"].length > 0 ? row["reason"] : null;
|
|
@@ -3290,6 +3330,8 @@ function planFromRow(row) {
|
|
|
3290
3330
|
minutes: num2(row["minutes"]) ?? 0,
|
|
3291
3331
|
openedAt: str(row["opened_at"]),
|
|
3292
3332
|
endsAt: str(row["ends_at"]),
|
|
3333
|
+
closingAt: str(row["closing_at"]),
|
|
3334
|
+
moveMode: row["move_mode"] === "auto" ? "auto" : "invite",
|
|
3293
3335
|
closedAt: str(row["closed_at"]),
|
|
3294
3336
|
broadcasts: (Array.isArray(row["broadcasts"]) ? row["broadcasts"] : []).map(broadcastFrom).filter((b) => b !== null)
|
|
3295
3337
|
};
|
|
@@ -3441,9 +3483,17 @@ function autoAssign(identities, rooms, exclude = []) {
|
|
|
3441
3483
|
return out;
|
|
3442
3484
|
}
|
|
3443
3485
|
function breakoutRemainingMs(plan, now) {
|
|
3444
|
-
if (plan === null || plan.state !== "open"
|
|
3445
|
-
const
|
|
3446
|
-
|
|
3486
|
+
if (plan === null || plan.state !== "open") return null;
|
|
3487
|
+
const ends = [plan.endsAt, plan.closingAt].map((stamp) => stamp === null ? Number.NaN : Date.parse(stamp)).filter((time) => !Number.isNaN(time));
|
|
3488
|
+
if (ends.length === 0) return null;
|
|
3489
|
+
return Math.max(0, Math.min(...ends) - now);
|
|
3490
|
+
}
|
|
3491
|
+
var BREAKOUT_COUNTDOWN_MS = 3e4;
|
|
3492
|
+
function breakoutClosingMs(plan, now) {
|
|
3493
|
+
if (plan === null || plan.state !== "open") return null;
|
|
3494
|
+
const remaining = breakoutRemainingMs(plan, now);
|
|
3495
|
+
if (remaining === null) return null;
|
|
3496
|
+
return plan.closingAt !== null || remaining <= BREAKOUT_COUNTDOWN_MS ? remaining : null;
|
|
3447
3497
|
}
|
|
3448
3498
|
function defaultCaptionLanguage(settings, viewerLanguage) {
|
|
3449
3499
|
const configured = settings?.captionTranslationLanguage.trim() ?? "";
|
|
@@ -3690,6 +3740,47 @@ function recordingLabel(state) {
|
|
|
3690
3740
|
}
|
|
3691
3741
|
}
|
|
3692
3742
|
|
|
3743
|
+
// src/core/live-policy.ts
|
|
3744
|
+
var DEFAULT_LIVE_POLICY = {
|
|
3745
|
+
chatMode: "everyone",
|
|
3746
|
+
avLock: "off",
|
|
3747
|
+
timeLimitMinutes: 0,
|
|
3748
|
+
timeLimitWarningMinutes: 5,
|
|
3749
|
+
recordingConsent: "notice"
|
|
3750
|
+
};
|
|
3751
|
+
function oneOf(value, allowed, fallback) {
|
|
3752
|
+
return typeof value === "string" && allowed.includes(value) ? value : fallback;
|
|
3753
|
+
}
|
|
3754
|
+
function whole(value, fallback) {
|
|
3755
|
+
const n = typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
|
|
3756
|
+
return Number.isFinite(n) && n >= 0 ? Math.floor(n) : fallback;
|
|
3757
|
+
}
|
|
3758
|
+
function livePolicyFrom(raw) {
|
|
3759
|
+
const o = typeof raw === "object" && raw !== null ? raw : {};
|
|
3760
|
+
return {
|
|
3761
|
+
chatMode: oneOf(o["chat_mode"], ["everyone", "hosts_only", "off"], DEFAULT_LIVE_POLICY.chatMode),
|
|
3762
|
+
avLock: oneOf(o["attendee_av_lock"], ["off", "mic", "camera", "both"], DEFAULT_LIVE_POLICY.avLock),
|
|
3763
|
+
timeLimitMinutes: whole(o["time_limit_minutes"], 0),
|
|
3764
|
+
timeLimitWarningMinutes: whole(o["time_limit_warning_minutes"], DEFAULT_LIVE_POLICY.timeLimitWarningMinutes),
|
|
3765
|
+
recordingConsent: oneOf(
|
|
3766
|
+
o["recording_consent"],
|
|
3767
|
+
["notice", "ok_or_leave", "view_only_until_agree"],
|
|
3768
|
+
DEFAULT_LIVE_POLICY.recordingConsent
|
|
3769
|
+
)
|
|
3770
|
+
};
|
|
3771
|
+
}
|
|
3772
|
+
function samePolicy(a, b) {
|
|
3773
|
+
return a.chatMode === b.chatMode && a.avLock === b.avLock && a.timeLimitMinutes === b.timeLimitMinutes && a.timeLimitWarningMinutes === b.timeLimitWarningMinutes && a.recordingConsent === b.recordingConsent;
|
|
3774
|
+
}
|
|
3775
|
+
var MODERATORS = /* @__PURE__ */ new Set(["host", "cohost"]);
|
|
3776
|
+
function isLockedFor(policy, role, kind) {
|
|
3777
|
+
if (MODERATORS.has(role)) return false;
|
|
3778
|
+
return policy.avLock === "both" || (kind === "microphone" ? policy.avLock === "mic" : policy.avLock === "camera");
|
|
3779
|
+
}
|
|
3780
|
+
function consentPending(policy, role, recordingOn, given) {
|
|
3781
|
+
return recordingOn && !given && policy.recordingConsent !== "notice" && !MODERATORS.has(role);
|
|
3782
|
+
}
|
|
3783
|
+
|
|
3693
3784
|
// src/core/attendance.ts
|
|
3694
3785
|
var IDLE_MEDIA_REGION = {
|
|
3695
3786
|
permission: { microphone: "unknown", camera: "unknown" },
|
|
@@ -3760,6 +3851,11 @@ var EMPTY = {
|
|
|
3760
3851
|
presenter: null,
|
|
3761
3852
|
waitingCount: 0,
|
|
3762
3853
|
leavePolicy: DEFAULT_LEAVE_POLICY,
|
|
3854
|
+
livePolicy: DEFAULT_LIVE_POLICY,
|
|
3855
|
+
canReclaim: false,
|
|
3856
|
+
recordingConsentGiven: false,
|
|
3857
|
+
sessionStartedAt: null,
|
|
3858
|
+
mutedByHost: false,
|
|
3763
3859
|
moderationNotice: null,
|
|
3764
3860
|
actionNotice: null,
|
|
3765
3861
|
privateChatRequest: null,
|
|
@@ -3769,6 +3865,8 @@ var EMPTY = {
|
|
|
3769
3865
|
names: {},
|
|
3770
3866
|
wrapUp: null,
|
|
3771
3867
|
noteTakerRefusal: null,
|
|
3868
|
+
captionsOn: false,
|
|
3869
|
+
captionsRefusal: null,
|
|
3772
3870
|
media: IDLE_MEDIA,
|
|
3773
3871
|
mediaRegion: IDLE_MEDIA_REGION,
|
|
3774
3872
|
failure: null,
|
|
@@ -3973,7 +4071,12 @@ function createMeetStore(initial = {}) {
|
|
|
3973
4071
|
if (current.enabled === next.enabled && current.unavailable?.code === next.unavailable?.code && current.unavailable?.reason === next.unavailable?.reason && current.unavailable?.message === next.unavailable?.message) {
|
|
3974
4072
|
return;
|
|
3975
4073
|
}
|
|
3976
|
-
commit({
|
|
4074
|
+
commit({
|
|
4075
|
+
...state,
|
|
4076
|
+
media: { ...state.media, [kind]: next },
|
|
4077
|
+
// The person turned their own microphone on again: the host's mute is over.
|
|
4078
|
+
...kind === "microphone" && next.enabled && state.mutedByHost ? { mutedByHost: false } : {}
|
|
4079
|
+
});
|
|
3977
4080
|
},
|
|
3978
4081
|
reset() {
|
|
3979
4082
|
const phase = nextPhase(state.phase, { type: "reset", teardown: true }) ?? EMPTY.phase;
|
|
@@ -4199,10 +4302,11 @@ function moderationText(kind, payload) {
|
|
|
4199
4302
|
switch (kind) {
|
|
4200
4303
|
case "muted": {
|
|
4201
4304
|
const source = text2(payload["source"]) ?? "microphone";
|
|
4202
|
-
|
|
4305
|
+
const who = payload["by_role"] === "host" || payload["by_role"] === "cohost" ? "the host" : by;
|
|
4306
|
+
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}.`;
|
|
4203
4307
|
}
|
|
4204
4308
|
case "ask-unmute":
|
|
4205
|
-
return `${by}
|
|
4309
|
+
return `${by === "The host" ? "The host" : by} asked you to unmute.`;
|
|
4206
4310
|
case "removed":
|
|
4207
4311
|
return `${by} removed you from this meeting.`;
|
|
4208
4312
|
default:
|
|
@@ -4228,7 +4332,7 @@ function applyModeration(store, payload, now) {
|
|
|
4228
4332
|
grants: { ...snapshot.grants, role },
|
|
4229
4333
|
moderationNotice: {
|
|
4230
4334
|
kind: "role",
|
|
4231
|
-
text: role === "host" ? "You are now the host." : role === "cohost" ? "You
|
|
4335
|
+
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.",
|
|
4232
4336
|
at: now()
|
|
4233
4337
|
}
|
|
4234
4338
|
});
|
|
@@ -4240,7 +4344,10 @@ function applyModeration(store, payload, now) {
|
|
|
4240
4344
|
case "removed": {
|
|
4241
4345
|
const sentence = moderationText(kind, payload);
|
|
4242
4346
|
if (sentence === null) return ignored("a moderation event with no sentence");
|
|
4243
|
-
store.patch({
|
|
4347
|
+
store.patch({
|
|
4348
|
+
moderationNotice: { kind, text: sentence, at: now() },
|
|
4349
|
+
...kind === "muted" && (text2(payload["source"]) ?? "microphone") === "microphone" ? { mutedByHost: true } : {}
|
|
4350
|
+
});
|
|
4244
4351
|
return HANDLED;
|
|
4245
4352
|
}
|
|
4246
4353
|
default:
|
|
@@ -5062,6 +5169,7 @@ function applyDoorRow(store, attendee) {
|
|
|
5062
5169
|
function decidedState(state) {
|
|
5063
5170
|
return WAITING_ADMISSION_STATES.includes(state) ? "expired" : state;
|
|
5064
5171
|
}
|
|
5172
|
+
var LIVE_POLICY_POLL_MS = 3e3;
|
|
5065
5173
|
function createMeetingSession(options) {
|
|
5066
5174
|
const { manager, repository, store, identity } = options;
|
|
5067
5175
|
let channel = null;
|
|
@@ -5203,6 +5311,46 @@ function createMeetingSession(options) {
|
|
|
5203
5311
|
stopWaiting();
|
|
5204
5312
|
}
|
|
5205
5313
|
}
|
|
5314
|
+
let policyTimer = null;
|
|
5315
|
+
let policyReading = false;
|
|
5316
|
+
async function readLivePolicy() {
|
|
5317
|
+
const meeting = store.snapshot().meeting;
|
|
5318
|
+
const pass = options.resolvePass?.() ?? null;
|
|
5319
|
+
if (meeting === null || pass === null || policyReading) return;
|
|
5320
|
+
policyReading = true;
|
|
5321
|
+
try {
|
|
5322
|
+
const response = await options.api.request({
|
|
5323
|
+
path: `${MEET_ROUTES.state}?meeting_id=${encodeURIComponent(meeting.id)}`,
|
|
5324
|
+
method: "GET",
|
|
5325
|
+
requireAuth: false,
|
|
5326
|
+
operation: "meet.state(policy)",
|
|
5327
|
+
headers: { [MEETING_PASS_HEADER]: pass }
|
|
5328
|
+
});
|
|
5329
|
+
if (!response.ok) return;
|
|
5330
|
+
const read = await response.json();
|
|
5331
|
+
const next = livePolicyFrom(read.live_policy);
|
|
5332
|
+
const started = read.session_started_at ? Date.parse(read.session_started_at) : NaN;
|
|
5333
|
+
const snapshot = store.snapshot();
|
|
5334
|
+
const startedAt = Number.isFinite(started) ? started : snapshot.sessionStartedAt;
|
|
5335
|
+
if (!samePolicy(snapshot.livePolicy, next) || startedAt !== snapshot.sessionStartedAt) {
|
|
5336
|
+
store.patch({ livePolicy: next, sessionStartedAt: startedAt });
|
|
5337
|
+
}
|
|
5338
|
+
} catch {
|
|
5339
|
+
} finally {
|
|
5340
|
+
policyReading = false;
|
|
5341
|
+
}
|
|
5342
|
+
}
|
|
5343
|
+
function syncLivePolicyTimer() {
|
|
5344
|
+
const inCall = store.snapshot().phase === "in_call";
|
|
5345
|
+
if (inCall && policyTimer === null) {
|
|
5346
|
+
void readLivePolicy();
|
|
5347
|
+
policyTimer = setInterval(() => void readLivePolicy(), LIVE_POLICY_POLL_MS);
|
|
5348
|
+
} else if (!inCall && policyTimer !== null) {
|
|
5349
|
+
clearInterval(policyTimer);
|
|
5350
|
+
policyTimer = null;
|
|
5351
|
+
}
|
|
5352
|
+
}
|
|
5353
|
+
const unsubscribePolicy = store.subscribe(syncLivePolicyTimer);
|
|
5206
5354
|
async function readState2() {
|
|
5207
5355
|
if (reading !== null) return reading;
|
|
5208
5356
|
const meeting = store.snapshot().meeting;
|
|
@@ -5589,6 +5737,9 @@ function createMeetingSession(options) {
|
|
|
5589
5737
|
},
|
|
5590
5738
|
dispose() {
|
|
5591
5739
|
stopWaiting();
|
|
5740
|
+
unsubscribePolicy();
|
|
5741
|
+
if (policyTimer !== null) clearInterval(policyTimer);
|
|
5742
|
+
policyTimer = null;
|
|
5592
5743
|
channel?.close();
|
|
5593
5744
|
channel = null;
|
|
5594
5745
|
meetingChannel?.close();
|
|
@@ -6906,6 +7057,7 @@ function createRoomEngine(options) {
|
|
|
6906
7057
|
lastServerUrl = token.serverUrl;
|
|
6907
7058
|
connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;
|
|
6908
7059
|
if (token.leavePolicy !== void 0) store.patch({ leavePolicy: token.leavePolicy });
|
|
7060
|
+
store.patch({ canReclaim: token.canReclaim === true });
|
|
6909
7061
|
store.patch({
|
|
6910
7062
|
localIdentity: token.identity,
|
|
6911
7063
|
breakoutRoomId: token.breakoutRoomId ?? null,
|
|
@@ -7405,8 +7557,12 @@ var MODERATION_ACTIONS = [
|
|
|
7405
7557
|
"lower_hand",
|
|
7406
7558
|
"lower_all_hands",
|
|
7407
7559
|
"make_cohost",
|
|
7560
|
+
"make_presenter",
|
|
7561
|
+
"revoke_presenter",
|
|
7562
|
+
"set_av_lock",
|
|
7408
7563
|
"revoke_cohost",
|
|
7409
7564
|
"transfer_host",
|
|
7565
|
+
"reclaim_host",
|
|
7410
7566
|
"rename",
|
|
7411
7567
|
"remove",
|
|
7412
7568
|
"report"
|
|
@@ -8336,11 +8492,13 @@ function takeMoveRequest(store) {
|
|
|
8336
8492
|
|
|
8337
8493
|
// src/core/observation.ts
|
|
8338
8494
|
function observedRole(role, standing) {
|
|
8339
|
-
if (role === "host" || role === "cohost") return role;
|
|
8495
|
+
if (role === "host" || role === "cohost" || role === "presenter") return role;
|
|
8340
8496
|
return isLinkFollower(standing) ? "guest" : "participant";
|
|
8341
8497
|
}
|
|
8342
|
-
function observedDevice(track, permission = "unknown") {
|
|
8498
|
+
function observedDevice(track, permission = "unknown", held = null) {
|
|
8499
|
+
if (held === "locked") return "locked";
|
|
8343
8500
|
if (track.enabled) return "on";
|
|
8501
|
+
if (held === "muted_by_host") return "muted_by_host";
|
|
8344
8502
|
const reason = deviceUnavailableReason(track, permission);
|
|
8345
8503
|
return reason === null ? "muted_by_me" : `unavailable:${reason}`;
|
|
8346
8504
|
}
|
|
@@ -8377,10 +8535,20 @@ function observe(input) {
|
|
|
8377
8535
|
recording: snapshot?.recording === "recording" || snapshot?.recording === "stopping",
|
|
8378
8536
|
// S6c: the autoplay region is known only inside the room (no remote audio exists elsewhere).
|
|
8379
8537
|
audioBlocked: inCall && snapshot !== null ? snapshot.mediaRegion.audioOutput === "blocked" : null,
|
|
8380
|
-
camera: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.camera, snapshot.mediaRegion.permission.camera) : null,
|
|
8381
|
-
microphone: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.microphone, snapshot.mediaRegion.permission.microphone) : null
|
|
8538
|
+
camera: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.camera, snapshot.mediaRegion.permission.camera, lockedFor(snapshot, "camera", inCall)) : null,
|
|
8539
|
+
microphone: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.microphone, snapshot.mediaRegion.permission.microphone, lockedFor(snapshot, "microphone", inCall)) : null
|
|
8382
8540
|
};
|
|
8383
8541
|
}
|
|
8542
|
+
function lockedFor(snapshot, kind, inCall) {
|
|
8543
|
+
if (!inCall) return null;
|
|
8544
|
+
const role = effectiveRole(snapshot);
|
|
8545
|
+
if (isLockedFor(snapshot.livePolicy, role, kind)) return "locked";
|
|
8546
|
+
if (snapshot.livePolicy.recordingConsent === "view_only_until_agree" && consentPending(snapshot.livePolicy, role, snapshot.recording === "recording", snapshot.recordingConsentGiven)) {
|
|
8547
|
+
return "locked";
|
|
8548
|
+
}
|
|
8549
|
+
if (kind === "microphone" && snapshot.mutedByHost) return "muted_by_host";
|
|
8550
|
+
return null;
|
|
8551
|
+
}
|
|
8384
8552
|
function staticObservation(phase, reason = null) {
|
|
8385
8553
|
return {
|
|
8386
8554
|
phase,
|