@ai-matrx/meet 0.11.43 → 0.11.45
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 +12 -0
- package/dist/{collab-BNJNgXFB.d.cts → collab-D1TX5uf9.d.cts} +7 -0
- package/dist/{collab-BNJNgXFB.d.ts → collab-D1TX5uf9.d.ts} +7 -0
- package/dist/{components-BDtGzHf4.d.ts → components-CNwBgmYU.d.ts} +1 -1
- package/dist/{components-BxK993iW.d.cts → components-CkyDos0f.d.cts} +1 -1
- package/dist/core.cjs +206 -20
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +121 -4
- package/dist/core.d.ts +121 -4
- package/dist/core.js +206 -20
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +206 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +206 -20
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +554 -50
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +4 -4
- package/dist/react.d.ts +4 -4
- package/dist/react.js +960 -456
- package/dist/react.js.map +1 -1
- package/dist/skins/meet.cjs +374 -39
- package/dist/skins/meet.cjs.map +1 -1
- package/dist/skins/meet.d.cts +4 -3
- package/dist/skins/meet.d.ts +4 -3
- package/dist/skins/meet.js +721 -386
- package/dist/skins/meet.js.map +1 -1
- package/dist/styles.css +17 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog — `@ai-matrx/meet`
|
|
2
2
|
|
|
3
|
+
## 0.11.45
|
|
4
|
+
|
|
5
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
6
|
+
`git diff npm/meet/v0.11.44..npm/meet/v0.11.45 -- apps/shared/meet`).
|
|
7
|
+
No source changes intended and no consumer action required.
|
|
8
|
+
|
|
9
|
+
## 0.11.44
|
|
10
|
+
|
|
11
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
12
|
+
`git diff npm/meet/v0.11.43..npm/meet/v0.11.44 -- apps/shared/meet`).
|
|
13
|
+
No source changes intended and no consumer action required.
|
|
14
|
+
|
|
3
15
|
## 0.11.43
|
|
4
16
|
|
|
5
17
|
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
@@ -107,6 +107,11 @@ interface LobbyEntry {
|
|
|
107
107
|
readonly knockExpiresAt?: string | null | undefined;
|
|
108
108
|
readonly decidedAt: string | null;
|
|
109
109
|
readonly decidedBy: UserId | null;
|
|
110
|
+
/**
|
|
111
|
+
* The server's flag on this knock (`mod-removed-bypass`): `previously_removed` when its coarse
|
|
112
|
+
* signals match someone a host removed from this meeting. Advice for the host, never a block.
|
|
113
|
+
*/
|
|
114
|
+
readonly flag?: string | null | undefined;
|
|
110
115
|
}
|
|
111
116
|
type ConnectionQuality = "excellent" | "good" | "poor" | "lost" | "unknown";
|
|
112
117
|
/** One person in the room, as the UI needs them. Media state is a FACT here. */
|
|
@@ -403,6 +408,8 @@ interface MeetingAttendee {
|
|
|
403
408
|
/** When this person knocked, and when an unanswered knock lapses (null = never). */
|
|
404
409
|
readonly knockedAt?: string | null | undefined;
|
|
405
410
|
readonly knockExpiresAt?: string | null | undefined;
|
|
411
|
+
/** The server's flag on a knock (`metadata.knock_flag`), e.g. `previously_removed`. */
|
|
412
|
+
readonly knockFlag?: string | null | undefined;
|
|
406
413
|
readonly isAgent: boolean;
|
|
407
414
|
/** FIRST arrival in the room — a rejoin never moves it. */
|
|
408
415
|
readonly joinedAt: string | null;
|
|
@@ -107,6 +107,11 @@ interface LobbyEntry {
|
|
|
107
107
|
readonly knockExpiresAt?: string | null | undefined;
|
|
108
108
|
readonly decidedAt: string | null;
|
|
109
109
|
readonly decidedBy: UserId | null;
|
|
110
|
+
/**
|
|
111
|
+
* The server's flag on this knock (`mod-removed-bypass`): `previously_removed` when its coarse
|
|
112
|
+
* signals match someone a host removed from this meeting. Advice for the host, never a block.
|
|
113
|
+
*/
|
|
114
|
+
readonly flag?: string | null | undefined;
|
|
110
115
|
}
|
|
111
116
|
type ConnectionQuality = "excellent" | "good" | "poor" | "lost" | "unknown";
|
|
112
117
|
/** One person in the room, as the UI needs them. Media state is a FACT here. */
|
|
@@ -403,6 +408,8 @@ interface MeetingAttendee {
|
|
|
403
408
|
/** When this person knocked, and when an unanswered knock lapses (null = never). */
|
|
404
409
|
readonly knockedAt?: string | null | undefined;
|
|
405
410
|
readonly knockExpiresAt?: string | null | undefined;
|
|
411
|
+
/** The server's flag on a knock (`metadata.knock_flag`), e.g. `previously_removed`. */
|
|
412
|
+
readonly knockFlag?: string | null | undefined;
|
|
406
413
|
readonly isAgent: boolean;
|
|
407
414
|
/** FIRST arrival in the room — a rejoin never moves it. */
|
|
408
415
|
readonly joinedAt: string | null;
|
|
@@ -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-D1TX5uf9.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* THE PRODUCT UI (R6/R7). Not a headless kernel with a note telling the host to
|
|
@@ -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-D1TX5uf9.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* THE PRODUCT UI (R6/R7). Not a headless kernel with a note telling the host to
|
package/dist/core.cjs
CHANGED
|
@@ -146,6 +146,7 @@ __export(core_exports, {
|
|
|
146
146
|
createReconnectPolicy: () => createReconnectPolicy,
|
|
147
147
|
createRecordingController: () => createRecordingController,
|
|
148
148
|
createRoomEngine: () => createRoomEngine,
|
|
149
|
+
createSpeakingWhileMutedDetector: () => createSpeakingWhileMutedDetector,
|
|
149
150
|
createTokenClient: () => createTokenClient,
|
|
150
151
|
createWebDeviceStorage: () => createWebDeviceStorage,
|
|
151
152
|
createWebEffectsStorage: () => createWebEffectsStorage,
|
|
@@ -154,6 +155,7 @@ __export(core_exports, {
|
|
|
154
155
|
createWebRoomTokenStorage: () => createWebRoomTokenStorage,
|
|
155
156
|
defaultCaptionLanguage: () => defaultCaptionLanguage,
|
|
156
157
|
detectBrowser: () => detectBrowser,
|
|
158
|
+
detectCapabilities: () => detectCapabilities,
|
|
157
159
|
detectOs: () => detectOs,
|
|
158
160
|
deviceChipLabel: () => deviceChipLabel,
|
|
159
161
|
deviceFixSteps: () => deviceFixSteps,
|
|
@@ -178,6 +180,7 @@ __export(core_exports, {
|
|
|
178
180
|
isLinkFollower: () => isLinkFollower,
|
|
179
181
|
isListenOnly: () => isListenOnly,
|
|
180
182
|
isLivePhase: () => isLivePhase,
|
|
183
|
+
isMobileUserAgent: () => isMobileUserAgent,
|
|
181
184
|
isNoteTaker: () => isNoteTaker,
|
|
182
185
|
isObserverRole: () => isObserverRole,
|
|
183
186
|
isRealName: () => isRealName,
|
|
@@ -269,6 +272,7 @@ __export(core_exports, {
|
|
|
269
272
|
transcriptDocument: () => transcriptDocument,
|
|
270
273
|
unavailableFrom: () => unavailableFrom,
|
|
271
274
|
uniqueAttendees: () => uniqueAttendees,
|
|
275
|
+
unsupportedWords: () => unsupportedWords,
|
|
272
276
|
untranslatedLines: () => untranslatedLines,
|
|
273
277
|
userTopic: () => userTopic,
|
|
274
278
|
wasRefused: () => wasRefused,
|
|
@@ -603,7 +607,8 @@ function createWebResumeStorage() {
|
|
|
603
607
|
}
|
|
604
608
|
};
|
|
605
609
|
}
|
|
606
|
-
var CLEARS_RESUME = (phase) => phase === "left" || phase === "removed" || phase === "superseded" || phase === "inactive" || phase === "ended" || phase === "record" || phase === "prejoin" || phase
|
|
610
|
+
var CLEARS_RESUME = (phase) => phase === "left" || phase === "removed" || phase === "superseded" || phase === "inactive" || phase === "ended" || phase === "record" || phase === "prejoin" || phase === "denied" || phase === "knock_expired" || phase.startsWith("refused:");
|
|
611
|
+
var WAITING_RESUMES = (phase) => phase.startsWith("waiting:");
|
|
607
612
|
function autoRejoinDelayMs(attempt, random = Math.random) {
|
|
608
613
|
const base = Math.min(15e3, 1e3 * 2 ** Math.max(0, attempt));
|
|
609
614
|
return Math.round(base * (0.5 + random()));
|
|
@@ -735,6 +740,10 @@ function createConnectionSupervisor(options) {
|
|
|
735
740
|
rejoinAttempts = 0;
|
|
736
741
|
stopRejoin();
|
|
737
742
|
saveMarker(true);
|
|
743
|
+
} else if (WAITING_RESUMES(phase)) {
|
|
744
|
+
stopRejoin();
|
|
745
|
+
driverRegion = null;
|
|
746
|
+
saveMarker(true);
|
|
738
747
|
} else if (phase === "disconnected") {
|
|
739
748
|
driverRegion = null;
|
|
740
749
|
saveMarker(true);
|
|
@@ -1038,6 +1047,7 @@ function parseRoomToken(body, roomName) {
|
|
|
1038
1047
|
connectionIdentity: readString(payload, "connection_identity") ?? personKey,
|
|
1039
1048
|
connectionKind: readString(payload, "connection_kind") === "companion" ? "companion" : "main",
|
|
1040
1049
|
secondDeviceChoice,
|
|
1050
|
+
secondDeviceRule: readString(payload, "second_device_rule"),
|
|
1041
1051
|
displayName: readString(payload, "display_name") ?? readString(payload, "displayName") ?? identity,
|
|
1042
1052
|
expiresAt,
|
|
1043
1053
|
canPublish: readBoolean(payload, "can_publish", true),
|
|
@@ -1776,6 +1786,12 @@ var JOINABLE_FROM = /* @__PURE__ */ new Set([
|
|
|
1776
1786
|
// allows the press; the server's gate decides whether it knocks or is refused again.
|
|
1777
1787
|
"refused:lobby_rejects"
|
|
1778
1788
|
]);
|
|
1789
|
+
var RETRYABLE_REFUSALS = /* @__PURE__ */ new Set([
|
|
1790
|
+
"refused:locked",
|
|
1791
|
+
"refused:too_early",
|
|
1792
|
+
"refused:meeting_full",
|
|
1793
|
+
"refused:platform_capacity"
|
|
1794
|
+
]);
|
|
1779
1795
|
var NOT_ENDABLE = /* @__PURE__ */ new Set([
|
|
1780
1796
|
"ended",
|
|
1781
1797
|
"record",
|
|
@@ -1841,7 +1857,7 @@ function nextPhase(from, event) {
|
|
|
1841
1857
|
case "link_state":
|
|
1842
1858
|
return from === "resolving" && event.reason !== null && isJoinRefusalReason(event.reason) ? refusalPhase(event.reason) : null;
|
|
1843
1859
|
case "join":
|
|
1844
|
-
return JOINABLE_FROM.has(from) ? "joining" : null;
|
|
1860
|
+
return JOINABLE_FROM.has(from) || RETRYABLE_REFUSALS.has(from) ? "joining" : null;
|
|
1845
1861
|
case "auto_rejoin":
|
|
1846
1862
|
return from === "disconnected" ? "joining" : null;
|
|
1847
1863
|
case "resumed":
|
|
@@ -2266,6 +2282,10 @@ var RPCS = {
|
|
|
2266
2282
|
admitAll: "meet_admit_all",
|
|
2267
2283
|
setLock: "meet_set_lock",
|
|
2268
2284
|
recordConsent: "meet_record_consent",
|
|
2285
|
+
/** One meeting rule as the gate obeys it (the knob ladder, this meeting's rung included). */
|
|
2286
|
+
meetingRule: "meet_policy",
|
|
2287
|
+
/** A host sets one rule for THIS meeting (the `meet_meeting` rung); refusals come back as `{ok:false}`. */
|
|
2288
|
+
setMeetingRule: "meet_policy_set",
|
|
2269
2289
|
/*
|
|
2270
2290
|
* ── MEET WAVE 1 — management (0.8). Every write is a SECURITY DEFINER door
|
|
2271
2291
|
* authorized by the meeting's host or a real iam grant (editor to change,
|
|
@@ -2312,7 +2332,7 @@ var INVITEE_COLUMNS = "id,meeting_id,invitee_user_id,email,display_name,role,rsv
|
|
|
2312
2332
|
var RECORDING_COLUMNS = "id,meeting_id,state,started_at,ended_at,started_by,file_id,failure_reason";
|
|
2313
2333
|
var SEGMENT_COLUMNS = "id,meeting_id,identity,speaker,text,started_at,is_final,metadata";
|
|
2314
2334
|
var NOTE_COLUMNS = "id,meeting_id,text,kind,assignee_user_id,created_at,metadata";
|
|
2315
|
-
var ATTENDEE_COLUMNS = "id,meeting_id,identity,participant_user_id,display_name,avatar_url,role,admission_state,knocked_at,knock_expires_at,is_agent,joined_at,left_at,sessions:metadata->sessions";
|
|
2335
|
+
var ATTENDEE_COLUMNS = "id,meeting_id,identity,participant_user_id,display_name,avatar_url,role,admission_state,knocked_at,knock_expires_at,is_agent,joined_at,left_at,sessions:metadata->sessions,knock_flag:metadata->>knock_flag";
|
|
2316
2336
|
function projectStays(value) {
|
|
2317
2337
|
if (!Array.isArray(value)) return [];
|
|
2318
2338
|
const stays = [];
|
|
@@ -2712,6 +2732,8 @@ function createMeetRepository(options) {
|
|
|
2712
2732
|
admissionState: states.includes(admission) ? admission : "admitted",
|
|
2713
2733
|
knockedAt: text(row, "knocked_at"),
|
|
2714
2734
|
knockExpiresAt: text(row, "knock_expires_at"),
|
|
2735
|
+
// The list read selects `metadata->>knock_flag`; a Postgres Changes row carries `metadata`.
|
|
2736
|
+
knockFlag: text(row, "knock_flag") ?? (typeof row["metadata"] === "object" && row["metadata"] !== null ? text(row["metadata"], "knock_flag") : null),
|
|
2715
2737
|
isAgent: bool(row, "is_agent", false),
|
|
2716
2738
|
joinedAt: text(row, "joined_at"),
|
|
2717
2739
|
leftAt: text(row, "left_at"),
|
|
@@ -3035,6 +3057,25 @@ function createMeetRepository(options) {
|
|
|
3035
3057
|
);
|
|
3036
3058
|
return projectMeeting(row);
|
|
3037
3059
|
},
|
|
3060
|
+
async meetingRule(meetingId, key) {
|
|
3061
|
+
const response = await db.rpc(RPCS.meetingRule, { p_meeting_id: meetingId, p_key: key });
|
|
3062
|
+
if (response.error !== null) throw normalizeMeetError(response.error, "meet.meetingRule");
|
|
3063
|
+
return response.data;
|
|
3064
|
+
},
|
|
3065
|
+
async setMeetingRule(meetingId, key, value) {
|
|
3066
|
+
const response = await db.rpc(RPCS.setMeetingRule, { p_meeting_id: meetingId, p_key: key, p_value: value });
|
|
3067
|
+
if (response.error !== null) throw normalizeMeetError(response.error, "meet.setMeetingRule");
|
|
3068
|
+
const answer = response.data ?? {};
|
|
3069
|
+
if (answer["ok"] !== true) {
|
|
3070
|
+
throw new MeetError(
|
|
3071
|
+
"forbidden",
|
|
3072
|
+
typeof answer["detail"] === "string" ? answer["detail"] : "This meeting's rule was not changed.",
|
|
3073
|
+
"Only the meeting's host or a co-host sets its rules; an organization can lock a rule for everyone.",
|
|
3074
|
+
answer
|
|
3075
|
+
);
|
|
3076
|
+
}
|
|
3077
|
+
return answer["effective_value"];
|
|
3078
|
+
},
|
|
3038
3079
|
async transcript(meetingId, limit = 1e3) {
|
|
3039
3080
|
const response = await db.from(TABLES.transcripts).select(SEGMENT_COLUMNS).eq("meeting_id", meetingId).order("started_at", { ascending: true }).order("id", { ascending: true }).limit(limit);
|
|
3040
3081
|
if (response.error !== null) throw normalizeMeetError(response.error, "meet.transcript");
|
|
@@ -3782,11 +3823,13 @@ function consentPending(policy, role, recordingOn, given) {
|
|
|
3782
3823
|
}
|
|
3783
3824
|
|
|
3784
3825
|
// src/core/attendance.ts
|
|
3826
|
+
var IDLE_PREVIEW = { state: "idle", unavailable: null };
|
|
3785
3827
|
var IDLE_MEDIA_REGION = {
|
|
3786
3828
|
permission: { microphone: "unknown", camera: "unknown" },
|
|
3787
3829
|
audioOutput: "ok",
|
|
3788
3830
|
notice: null,
|
|
3789
|
-
offer: null
|
|
3831
|
+
offer: null,
|
|
3832
|
+
preview: { microphone: IDLE_PREVIEW, camera: IDLE_PREVIEW }
|
|
3790
3833
|
};
|
|
3791
3834
|
var IDLE_TRACK = { enabled: false, unavailable: null };
|
|
3792
3835
|
var IDLE_MEDIA = {
|
|
@@ -4713,6 +4756,47 @@ function gridColumns(count2) {
|
|
|
4713
4756
|
return 5;
|
|
4714
4757
|
}
|
|
4715
4758
|
|
|
4759
|
+
// src/core/capabilities.ts
|
|
4760
|
+
var IN_APP = [
|
|
4761
|
+
[/Instagram/i, "Instagram"],
|
|
4762
|
+
[/FBAN|FBAV|FB_IAB/i, "Facebook"],
|
|
4763
|
+
[/LinkedInApp/i, "LinkedIn"],
|
|
4764
|
+
[/MicroMessenger/i, "WeChat"],
|
|
4765
|
+
[/Snapchat/i, "Snapchat"],
|
|
4766
|
+
[/\bLine\//i, "LINE"],
|
|
4767
|
+
[/Twitter|TwitterAndroid/i, "X"],
|
|
4768
|
+
[/TikTok|musical_ly|BytedanceWebview/i, "TikTok"]
|
|
4769
|
+
];
|
|
4770
|
+
function isMobileUserAgent(userAgent, maxTouchPoints = 0) {
|
|
4771
|
+
if (/iPhone|iPod|Android|Mobile|iPad/i.test(userAgent)) return true;
|
|
4772
|
+
return /Macintosh/.test(userAgent) && maxTouchPoints > 1;
|
|
4773
|
+
}
|
|
4774
|
+
function detectCapabilities(scope = typeof globalThis === "undefined" ? null : globalThis) {
|
|
4775
|
+
if (scope === null || scope.navigator === void 0) {
|
|
4776
|
+
return { canJoin: true, reason: null, inAppBrowser: null, mobile: false, canShareScreen: false };
|
|
4777
|
+
}
|
|
4778
|
+
const ua = scope.navigator.userAgent ?? "";
|
|
4779
|
+
const webrtc = typeof scope.RTCPeerConnection === "function" || typeof scope.webkitRTCPeerConnection === "function";
|
|
4780
|
+
const mobile = isMobileUserAgent(ua, scope.navigator.maxTouchPoints ?? 0);
|
|
4781
|
+
const inAppBrowser = IN_APP.find(([pattern]) => pattern.test(ua))?.[1] ?? null;
|
|
4782
|
+
return {
|
|
4783
|
+
canJoin: webrtc,
|
|
4784
|
+
reason: webrtc ? null : "no_webrtc",
|
|
4785
|
+
inAppBrowser,
|
|
4786
|
+
mobile,
|
|
4787
|
+
canShareScreen: !mobile && typeof scope.navigator.mediaDevices?.getDisplayMedia === "function"
|
|
4788
|
+
};
|
|
4789
|
+
}
|
|
4790
|
+
function unsupportedWords(capabilities) {
|
|
4791
|
+
if (capabilities.inAppBrowser !== null) {
|
|
4792
|
+
return {
|
|
4793
|
+
title: `${capabilities.inAppBrowser}'s browser can't join calls`,
|
|
4794
|
+
body: "Open this link in Chrome, Safari, Edge or Firefox"
|
|
4795
|
+
};
|
|
4796
|
+
}
|
|
4797
|
+
return { title: "This browser can't join calls", body: "Open this link in Chrome, Safari, Edge or Firefox" };
|
|
4798
|
+
}
|
|
4799
|
+
|
|
4716
4800
|
// src/core/room-driver.ts
|
|
4717
4801
|
var DATA_TOPICS = {
|
|
4718
4802
|
reaction: "mx.reaction",
|
|
@@ -4867,7 +4951,15 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
4867
4951
|
created.on(RoomEvent.ParticipantConnected, roster).on(RoomEvent.ParticipantDisconnected, (participant, reason) => {
|
|
4868
4952
|
handlers.participantLeft?.(participant.identity, reasonName(reason));
|
|
4869
4953
|
roster();
|
|
4870
|
-
}).on(RoomEvent.TrackSubscribed, roster).on(RoomEvent.TrackUnsubscribed, roster).on(RoomEvent.TrackMuted, roster).on(RoomEvent.TrackUnmuted, roster).on(RoomEvent.LocalTrackPublished,
|
|
4954
|
+
}).on(RoomEvent.TrackSubscribed, roster).on(RoomEvent.TrackUnsubscribed, roster).on(RoomEvent.TrackMuted, roster).on(RoomEvent.TrackUnmuted, roster).on(RoomEvent.LocalTrackPublished, (publication) => {
|
|
4955
|
+
if (publication.source === Track.Source.ScreenShare) {
|
|
4956
|
+
const ended = () => {
|
|
4957
|
+
void created.localParticipant.setScreenShareEnabled(false).catch(() => void 0);
|
|
4958
|
+
};
|
|
4959
|
+
publication.track?.mediaStreamTrack.addEventListener("ended", ended, { once: true });
|
|
4960
|
+
}
|
|
4961
|
+
roster();
|
|
4962
|
+
}).on(RoomEvent.LocalTrackUnpublished, roster).on(RoomEvent.ParticipantNameChanged, roster).on(RoomEvent.ParticipantAttributesChanged, roster).on(RoomEvent.ParticipantMetadataChanged, roster).on(RoomEvent.RoomMetadataChanged, (metadata) => {
|
|
4871
4963
|
handlers.roomMetadata?.(metadata ?? "");
|
|
4872
4964
|
}).on(RoomEvent.AudioPlaybackStatusChanged, (canPlay) => {
|
|
4873
4965
|
handlers.audioPlayback?.(canPlay);
|
|
@@ -5008,7 +5100,10 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
5008
5100
|
},
|
|
5009
5101
|
async setScreenShareEnabled(enabled) {
|
|
5010
5102
|
try {
|
|
5011
|
-
await requireRoom("setScreenShareEnabled").localParticipant.setScreenShareEnabled(
|
|
5103
|
+
await requireRoom("setScreenShareEnabled").localParticipant.setScreenShareEnabled(
|
|
5104
|
+
enabled,
|
|
5105
|
+
enabled ? { audio: true, systemAudio: "include" } : void 0
|
|
5106
|
+
);
|
|
5012
5107
|
} catch (error) {
|
|
5013
5108
|
throw normalizeScreenShareError(error, "meet.setScreenShareEnabled");
|
|
5014
5109
|
}
|
|
@@ -5047,8 +5142,13 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
5047
5142
|
supportsVideoEffects() {
|
|
5048
5143
|
return videoEffectsSupported();
|
|
5049
5144
|
},
|
|
5145
|
+
screenShareHasAudio() {
|
|
5146
|
+
const target = room;
|
|
5147
|
+
if (target === null) return false;
|
|
5148
|
+
return target.localParticipant.getTrackPublications().some((p) => String(p.source) === "screen_share_audio" && p.track !== void 0);
|
|
5149
|
+
},
|
|
5050
5150
|
supportsScreenShare() {
|
|
5051
|
-
return
|
|
5151
|
+
return detectCapabilities().canShareScreen;
|
|
5052
5152
|
},
|
|
5053
5153
|
attach(identity, kind, element) {
|
|
5054
5154
|
const target = requireRoom("attach");
|
|
@@ -5141,7 +5241,8 @@ function lobbyEntryFrom(attendee, now = Date.now()) {
|
|
|
5141
5241
|
knockedAt: attendee.knockedAt ?? attendee.joinedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
5142
5242
|
knockExpiresAt: attendee.knockExpiresAt ?? null,
|
|
5143
5243
|
decidedAt: null,
|
|
5144
|
-
decidedBy: null
|
|
5244
|
+
decidedBy: null,
|
|
5245
|
+
flag: attendee.knockFlag ?? null
|
|
5145
5246
|
};
|
|
5146
5247
|
}
|
|
5147
5248
|
function deniedEntryFrom(attendee) {
|
|
@@ -5673,6 +5774,17 @@ function createMeetingSession(options) {
|
|
|
5673
5774
|
async askAgain() {
|
|
5674
5775
|
await options.rejoin?.();
|
|
5675
5776
|
},
|
|
5777
|
+
async meetingRule(key) {
|
|
5778
|
+
const meeting = requireMeeting("meet.meetingRule");
|
|
5779
|
+
return repository.meetingRule(meeting.id, key);
|
|
5780
|
+
},
|
|
5781
|
+
async setMeetingRule(key, value) {
|
|
5782
|
+
requireHostUser("meet.setMeetingRule");
|
|
5783
|
+
const meeting = requireMeeting("meet.setMeetingRule");
|
|
5784
|
+
const effective = await repository.setMeetingRule(meeting.id, key, value);
|
|
5785
|
+
hint();
|
|
5786
|
+
return effective;
|
|
5787
|
+
},
|
|
5676
5788
|
async setLock(locked) {
|
|
5677
5789
|
const by = requireHostUser("meet.setLock");
|
|
5678
5790
|
const meeting = requireMeeting("meet.setLock");
|
|
@@ -6353,7 +6465,7 @@ function deviceFixSteps(kind, reason, where) {
|
|
|
6353
6465
|
case "safari":
|
|
6354
6466
|
return where.os === "ios" ? [`Settings \u203A Apps \u203A Safari \u203A ${device}`, "Choose Allow", "Return here and press Try again"] : ["Safari \u203A Settings for This Website", `Set ${device} to Allow`, "Press Try again"];
|
|
6355
6467
|
default:
|
|
6356
|
-
return ["Click the icon
|
|
6468
|
+
return ["Click the site settings icon in the address bar", `Set ${device} to Allow`, "Press Try again"];
|
|
6357
6469
|
}
|
|
6358
6470
|
case "blocked_os": {
|
|
6359
6471
|
const app = where.browser === "chrome" ? "Google Chrome" : where.browser === "edge" ? "Microsoft Edge" : where.browser === "firefox" ? "Firefox" : "your browser";
|
|
@@ -6451,7 +6563,7 @@ function createMediaSupervisor(options) {
|
|
|
6451
6563
|
function setRegion(next) {
|
|
6452
6564
|
const current = region();
|
|
6453
6565
|
const merged = { ...current, ...next };
|
|
6454
|
-
if (merged.audioOutput === current.audioOutput && merged.notice === current.notice && merged.offer === current.offer && merged.permission.microphone === current.permission.microphone && merged.permission.camera === current.permission.camera) {
|
|
6566
|
+
if (merged.audioOutput === current.audioOutput && merged.notice === current.notice && merged.offer === current.offer && merged.preview === current.preview && merged.permission.microphone === current.permission.microphone && merged.permission.camera === current.permission.camera) {
|
|
6455
6567
|
return;
|
|
6456
6568
|
}
|
|
6457
6569
|
store.patch({ mediaRegion: merged });
|
|
@@ -6547,7 +6659,7 @@ function createMediaSupervisor(options) {
|
|
|
6547
6659
|
if (permission !== "unknown") setPermission(kind, permission);
|
|
6548
6660
|
return unavailableFrom(normalized, classifyDeviceFailure(normalized, permission));
|
|
6549
6661
|
}
|
|
6550
|
-
|
|
6662
|
+
const api = {
|
|
6551
6663
|
start() {
|
|
6552
6664
|
if (started || platform === null) return;
|
|
6553
6665
|
started = true;
|
|
@@ -6569,6 +6681,16 @@ function createMediaSupervisor(options) {
|
|
|
6569
6681
|
started = false;
|
|
6570
6682
|
},
|
|
6571
6683
|
unavailable,
|
|
6684
|
+
async preview(kind, outcome) {
|
|
6685
|
+
api.start();
|
|
6686
|
+
let next;
|
|
6687
|
+
if (typeof outcome === "string") next = { state: outcome, unavailable: null };
|
|
6688
|
+
else next = { state: "unavailable", unavailable: await unavailable(outcome.error, kind, `meet.${kind}Preview`) };
|
|
6689
|
+
const current = region().preview;
|
|
6690
|
+
const was = current[kind];
|
|
6691
|
+
if (was.state === next.state && was.unavailable?.reason === next.unavailable?.reason && was.unavailable?.message === next.unavailable?.message) return;
|
|
6692
|
+
setRegion({ preview: { ...region().preview, [kind]: next } });
|
|
6693
|
+
},
|
|
6572
6694
|
onAudioPlayback(canPlay) {
|
|
6573
6695
|
setRegion({ audioOutput: canPlay ? "ok" : "blocked" });
|
|
6574
6696
|
},
|
|
@@ -6627,6 +6749,7 @@ function createMediaSupervisor(options) {
|
|
|
6627
6749
|
setRegion({ notice: null });
|
|
6628
6750
|
}
|
|
6629
6751
|
};
|
|
6752
|
+
return api;
|
|
6630
6753
|
}
|
|
6631
6754
|
|
|
6632
6755
|
// src/core/room.ts
|
|
@@ -6788,7 +6911,8 @@ function createRoomEngine(options) {
|
|
|
6788
6911
|
message: normalized.message,
|
|
6789
6912
|
remedy: normalized.remedy,
|
|
6790
6913
|
reason: normalized.reason,
|
|
6791
|
-
retryAt: normalized.retryAfterSeconds === null ? null : now() + normalized.retryAfterSeconds * 1e3
|
|
6914
|
+
retryAt: normalized.retryAfterSeconds === null ? null : now() + normalized.retryAfterSeconds * 1e3,
|
|
6915
|
+
stage: "join"
|
|
6792
6916
|
}
|
|
6793
6917
|
});
|
|
6794
6918
|
store.dispatch({ type: "join_failed", reason: normalized.reason ?? null });
|
|
@@ -7052,7 +7176,7 @@ function createRoomEngine(options) {
|
|
|
7052
7176
|
const { secondDevice: _answered, ...rest } = lastRequest;
|
|
7053
7177
|
lastRequest = rest;
|
|
7054
7178
|
}
|
|
7055
|
-
options.tabClaim?.inRoom(String(request.roomName), String(token.identity));
|
|
7179
|
+
options.tabClaim?.inRoom(String(request.roomName), String(token.identity), token.secondDeviceRule ?? null);
|
|
7056
7180
|
current = token;
|
|
7057
7181
|
lastServerUrl = token.serverUrl;
|
|
7058
7182
|
connectionPolicy = token.connectionPolicy ?? DEFAULT_CONNECTION_POLICY;
|
|
@@ -7179,6 +7303,10 @@ function createRoomEngine(options) {
|
|
|
7179
7303
|
}
|
|
7180
7304
|
departures.clear();
|
|
7181
7305
|
liveRoster = list;
|
|
7306
|
+
const localNow = list.find((p) => p.isLocal);
|
|
7307
|
+
if (localNow !== void 0 && !localNow.screenSharing && store.snapshot().share.state === "presenting") {
|
|
7308
|
+
store.patch({ share: NO_SHARE });
|
|
7309
|
+
}
|
|
7182
7310
|
const merged = mergeGhosts(list, ghosts, at);
|
|
7183
7311
|
ghosts = merged.kept;
|
|
7184
7312
|
const people = /* @__PURE__ */ new Map();
|
|
@@ -7360,10 +7488,12 @@ function createRoomEngine(options) {
|
|
|
7360
7488
|
if (claim === void 0 || userId === null) return false;
|
|
7361
7489
|
const before = store.snapshot();
|
|
7362
7490
|
if (before.phase !== "prejoin" || before.secondDevice !== null) return false;
|
|
7363
|
-
|
|
7491
|
+
const person = `user:${String(userId)}`;
|
|
7492
|
+
const found = claim.presenceElsewhere !== void 0 ? await claim.presenceElsewhere(String(roomName), person) : await claim.presentElsewhere(String(roomName), person) ? { rule: null } : null;
|
|
7493
|
+
if (found === null || found.rule === "allow_both") return false;
|
|
7364
7494
|
const now2 = store.snapshot();
|
|
7365
7495
|
if (now2.phase !== "prejoin" || now2.secondDevice !== null) return false;
|
|
7366
|
-
store.patch({ secondDevice: { rule: "ask_switch_or_join" } });
|
|
7496
|
+
store.patch({ secondDevice: { rule: found.rule ?? "ask_switch_or_join" } });
|
|
7367
7497
|
report("info", "meet: this browser already has you in this meeting \u2014 offering the choice before Join");
|
|
7368
7498
|
return true;
|
|
7369
7499
|
},
|
|
@@ -7398,7 +7528,10 @@ function createRoomEngine(options) {
|
|
|
7398
7528
|
try {
|
|
7399
7529
|
await driver.setScreenShareEnabled(enabled);
|
|
7400
7530
|
ambiguousShareDenials = 0;
|
|
7401
|
-
|
|
7531
|
+
const audio = enabled ? driver?.screenShareHasAudio?.() : void 0;
|
|
7532
|
+
store.patch({
|
|
7533
|
+
share: { state: enabled ? "presenting" : "none", reason: null, remedy: null, at: now(), ...audio !== void 0 ? { audio } : {} }
|
|
7534
|
+
});
|
|
7402
7535
|
} catch (error) {
|
|
7403
7536
|
const kind = classifyScreenShareError(error);
|
|
7404
7537
|
if (kind === "ambiguous") {
|
|
@@ -7465,13 +7598,14 @@ function createRoomEngine(options) {
|
|
|
7465
7598
|
return driver?.supportsVideoEffects?.() ?? false;
|
|
7466
7599
|
},
|
|
7467
7600
|
supportsScreenShare() {
|
|
7468
|
-
return driver?.supportsScreenShare?.() ??
|
|
7601
|
+
return driver?.supportsScreenShare?.() ?? detectCapabilities().canShareScreen;
|
|
7469
7602
|
},
|
|
7470
7603
|
async switchDevice(kind, deviceId) {
|
|
7471
7604
|
if (driver === null) return;
|
|
7472
7605
|
await driver.switchDevice(kind, deviceId);
|
|
7473
7606
|
},
|
|
7474
7607
|
enableAudio: () => media.enableAudio(),
|
|
7608
|
+
previewDevice: (kind, outcome) => media.preview(kind, outcome),
|
|
7475
7609
|
acceptDeviceOffer: () => media.acceptOffer(),
|
|
7476
7610
|
dismissDeviceOffer: () => media.dismissOffer(),
|
|
7477
7611
|
dismissDeviceNotice: () => media.dismissNotice(),
|
|
@@ -8502,6 +8636,21 @@ function observedDevice(track, permission = "unknown", held = null) {
|
|
|
8502
8636
|
const reason = deviceUnavailableReason(track, permission);
|
|
8503
8637
|
return reason === null ? "muted_by_me" : `unavailable:${reason}`;
|
|
8504
8638
|
}
|
|
8639
|
+
function observedPreview(preview, permission = "unknown") {
|
|
8640
|
+
if (permission === "denied") return "unavailable:denied";
|
|
8641
|
+
switch (preview.state) {
|
|
8642
|
+
case "on":
|
|
8643
|
+
return "on";
|
|
8644
|
+
case "off":
|
|
8645
|
+
return "muted_by_me";
|
|
8646
|
+
case "prompting":
|
|
8647
|
+
return "prompting";
|
|
8648
|
+
case "unavailable":
|
|
8649
|
+
return `unavailable:${preview.unavailable?.reason ?? "failed"}`;
|
|
8650
|
+
default:
|
|
8651
|
+
return null;
|
|
8652
|
+
}
|
|
8653
|
+
}
|
|
8505
8654
|
function endedRecord(snapshot, given) {
|
|
8506
8655
|
const record = snapshot?.meeting ?? given;
|
|
8507
8656
|
return record !== null && record.endedAt !== null;
|
|
@@ -8525,18 +8674,22 @@ function observe(input) {
|
|
|
8525
8674
|
const canAdmit = actual !== "none" && ADMITTER_ROLES.has(actual);
|
|
8526
8675
|
const role = actual === "none" || snapshot === null ? "none" : observedRole(actual, effectiveStanding(snapshot));
|
|
8527
8676
|
const deviceInPlay = phase === "joining" || inCall;
|
|
8677
|
+
const region = snapshot?.mediaRegion;
|
|
8678
|
+
const atPrejoin = phase === "prejoin" && region !== void 0;
|
|
8528
8679
|
return {
|
|
8529
8680
|
phase,
|
|
8530
8681
|
reason: phase === "ended" ? snapshot?.reason ?? null : snapshot?.reason ?? reasonOf(phase),
|
|
8531
8682
|
role,
|
|
8532
|
-
|
|
8683
|
+
// `net-blip`: in the call, a drop shorter than `reconnect_banner_delay_ms` is not something the
|
|
8684
|
+
// person sees — the contract says what is on screen, so it reads `stable` until the banner shows.
|
|
8685
|
+
connection: snapshot === null ? "stable" : inCall && snapshot.connection !== "stable" && !snapshot.connectionBanner ? "stable" : snapshot.connection,
|
|
8533
8686
|
hostPresent: inCall && snapshot !== null ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role)) : snapshot !== null && isWaitingPhase(snapshot.phase) ? snapshot.waiting?.admittersPresent ?? null : null,
|
|
8534
8687
|
lobbyCount: canAdmit && snapshot !== null ? snapshot.lobby.filter((entry) => entry.state === "knocking").length : 0,
|
|
8535
8688
|
recording: snapshot?.recording === "recording" || snapshot?.recording === "stopping",
|
|
8536
8689
|
// S6c: the autoplay region is known only inside the room (no remote audio exists elsewhere).
|
|
8537
8690
|
audioBlocked: inCall && snapshot !== null ? snapshot.mediaRegion.audioOutput === "blocked" : 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
|
|
8691
|
+
camera: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.camera, snapshot.mediaRegion.permission.camera, lockedFor(snapshot, "camera", inCall)) : atPrejoin ? observedPreview(region.preview.camera, region.permission.camera) : null,
|
|
8692
|
+
microphone: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.microphone, snapshot.mediaRegion.permission.microphone, lockedFor(snapshot, "microphone", inCall)) : atPrejoin ? observedPreview(region.preview.microphone, region.permission.microphone) : null
|
|
8540
8693
|
};
|
|
8541
8694
|
}
|
|
8542
8695
|
function lockedFor(snapshot, kind, inCall) {
|
|
@@ -8616,4 +8769,37 @@ function microphoneNoticeCode(track, permission = "unknown") {
|
|
|
8616
8769
|
if (reason === null) return null;
|
|
8617
8770
|
return deviceNoticeCode("microphone", reason) === "mic-blocked" ? "mic-blocked" : "device-lost";
|
|
8618
8771
|
}
|
|
8772
|
+
|
|
8773
|
+
// src/core/speaking-detector.ts
|
|
8774
|
+
function createSpeakingWhileMutedDetector(options = {}) {
|
|
8775
|
+
const threshold = options.threshold ?? 0.06;
|
|
8776
|
+
const holdMs = options.holdMs ?? 600;
|
|
8777
|
+
const gapMs = options.gapMs ?? 400;
|
|
8778
|
+
const cooldownMs = options.cooldownMs ?? 2e4;
|
|
8779
|
+
let speechStart = null;
|
|
8780
|
+
let lastLoud = null;
|
|
8781
|
+
let firedAt = null;
|
|
8782
|
+
return {
|
|
8783
|
+
feed(level, at) {
|
|
8784
|
+
if (level >= threshold) {
|
|
8785
|
+
if (speechStart === null || lastLoud === null || at - lastLoud > gapMs) speechStart = at;
|
|
8786
|
+
lastLoud = at;
|
|
8787
|
+
} else if (lastLoud !== null && at - lastLoud > gapMs) {
|
|
8788
|
+
speechStart = null;
|
|
8789
|
+
lastLoud = null;
|
|
8790
|
+
}
|
|
8791
|
+
if (speechStart === null || at - speechStart < holdMs) return false;
|
|
8792
|
+
if (firedAt !== null && at - firedAt < cooldownMs) return false;
|
|
8793
|
+
firedAt = at;
|
|
8794
|
+
speechStart = null;
|
|
8795
|
+
lastLoud = null;
|
|
8796
|
+
return true;
|
|
8797
|
+
},
|
|
8798
|
+
reset() {
|
|
8799
|
+
speechStart = null;
|
|
8800
|
+
lastLoud = null;
|
|
8801
|
+
firedAt = null;
|
|
8802
|
+
}
|
|
8803
|
+
};
|
|
8804
|
+
}
|
|
8619
8805
|
//# sourceMappingURL=core.cjs.map
|