@meshagent/meshagent-tailwind 0.41.2 → 0.41.5
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 +9 -0
- package/README.md +33 -0
- package/dist/cjs/chat/chat-thread.js +15 -10
- package/dist/cjs/chat/dataset-chat-thread.d.ts +4 -1
- package/dist/cjs/chat/dataset-chat-thread.js +130 -157
- package/dist/cjs/chat/multi-thread-view.d.ts +4 -1
- package/dist/cjs/chat/multi-thread-view.js +4 -0
- package/dist/cjs/chat/new-chat-thread.d.ts +4 -1
- package/dist/cjs/chat/new-chat-thread.js +43 -87
- package/dist/cjs/file-preview/file-preview.d.ts +6 -0
- package/dist/cjs/file-preview/file-preview.js +220 -0
- package/dist/cjs/meetings/camera-grid.d.ts +46 -0
- package/dist/cjs/meetings/camera-grid.js +435 -0
- package/dist/cjs/meetings/controls.d.ts +4 -2
- package/dist/cjs/meetings/controls.js +9 -3
- package/dist/cjs/meetings/lobby.d.ts +17 -0
- package/dist/cjs/meetings/lobby.js +595 -0
- package/dist/cjs/meetings/meeting-scope.d.ts +7 -6
- package/dist/cjs/meetings/meeting-scope.js +64 -15
- package/dist/cjs/meetings/meeting-view.d.ts +6 -0
- package/dist/cjs/meetings/meeting-view.js +635 -0
- package/dist/cjs/meetings/meetings.d.ts +3 -0
- package/dist/cjs/meetings/meetings.js +3 -0
- package/dist/cjs/meetings/wake-lock.js +2 -2
- package/dist/esm/chat/chat-thread.js +15 -10
- package/dist/esm/chat/dataset-chat-thread.d.ts +4 -1
- package/dist/esm/chat/dataset-chat-thread.js +129 -133
- package/dist/esm/chat/multi-thread-view.d.ts +4 -1
- package/dist/esm/chat/multi-thread-view.js +4 -0
- package/dist/esm/chat/new-chat-thread.d.ts +4 -1
- package/dist/esm/chat/new-chat-thread.js +43 -87
- package/dist/esm/file-preview/file-preview.d.ts +6 -0
- package/dist/esm/file-preview/file-preview.js +220 -0
- package/dist/esm/meetings/camera-grid.d.ts +46 -0
- package/dist/esm/meetings/camera-grid.js +405 -0
- package/dist/esm/meetings/controls.d.ts +4 -2
- package/dist/esm/meetings/controls.js +9 -3
- package/dist/esm/meetings/lobby.d.ts +17 -0
- package/dist/esm/meetings/lobby.js +595 -0
- package/dist/esm/meetings/meeting-scope.d.ts +7 -6
- package/dist/esm/meetings/meeting-scope.js +71 -16
- package/dist/esm/meetings/meeting-view.d.ts +6 -0
- package/dist/esm/meetings/meeting-view.js +630 -0
- package/dist/esm/meetings/meetings.d.ts +3 -0
- package/dist/esm/meetings/meetings.js +3 -0
- package/dist/esm/meetings/wake-lock.js +2 -2
- package/dist/index.css +1 -1
- package/package.json +9 -5
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var meeting_view_exports = {};
|
|
20
|
+
__export(meeting_view_exports, {
|
|
21
|
+
MeetingView: () => MeetingView
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(meeting_view_exports);
|
|
24
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
25
|
+
var import_livekit_client = require("livekit-client");
|
|
26
|
+
var import_lucide_react = require("lucide-react");
|
|
27
|
+
var import_react = require("react");
|
|
28
|
+
var import_button = require("../components/ui/button");
|
|
29
|
+
var import_spinner = require("../components/ui/spinner");
|
|
30
|
+
var import_utils = require("../lib/utils");
|
|
31
|
+
var import_audio_visualization = require("./audio-visualization");
|
|
32
|
+
var import_camera_grid = require("./camera-grid");
|
|
33
|
+
var import_controls = require("./controls");
|
|
34
|
+
var import_lobby = require("./lobby");
|
|
35
|
+
var import_meeting_scope = require("./meeting-scope");
|
|
36
|
+
const railGap = 16;
|
|
37
|
+
const desktopStripWidth = 250;
|
|
38
|
+
const desktopStripHeight = 100;
|
|
39
|
+
function useRoomSnapshot(room) {
|
|
40
|
+
(0, import_react.useSyncExternalStore)(
|
|
41
|
+
(listener) => {
|
|
42
|
+
const events = [
|
|
43
|
+
import_livekit_client.RoomEvent.ActiveSpeakersChanged,
|
|
44
|
+
import_livekit_client.RoomEvent.Connected,
|
|
45
|
+
import_livekit_client.RoomEvent.ConnectionStateChanged,
|
|
46
|
+
import_livekit_client.RoomEvent.Disconnected,
|
|
47
|
+
import_livekit_client.RoomEvent.LocalTrackPublished,
|
|
48
|
+
import_livekit_client.RoomEvent.LocalTrackUnpublished,
|
|
49
|
+
import_livekit_client.RoomEvent.ParticipantConnected,
|
|
50
|
+
import_livekit_client.RoomEvent.ParticipantDisconnected,
|
|
51
|
+
import_livekit_client.RoomEvent.TrackMuted,
|
|
52
|
+
import_livekit_client.RoomEvent.TrackPublished,
|
|
53
|
+
import_livekit_client.RoomEvent.TrackSubscribed,
|
|
54
|
+
import_livekit_client.RoomEvent.TrackUnmuted,
|
|
55
|
+
import_livekit_client.RoomEvent.TrackUnpublished,
|
|
56
|
+
import_livekit_client.RoomEvent.TrackUnsubscribed
|
|
57
|
+
];
|
|
58
|
+
for (const eventName of events) {
|
|
59
|
+
room.on(eventName, listener);
|
|
60
|
+
}
|
|
61
|
+
return () => {
|
|
62
|
+
for (const eventName of events) {
|
|
63
|
+
room.off(eventName, listener);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
() => {
|
|
68
|
+
const participants = [
|
|
69
|
+
room.localParticipant,
|
|
70
|
+
...Array.from(room.remoteParticipants.values())
|
|
71
|
+
];
|
|
72
|
+
const trackState = participants.map(
|
|
73
|
+
(participant) => [
|
|
74
|
+
participant.sid,
|
|
75
|
+
participant.identity,
|
|
76
|
+
participant.isCameraEnabled,
|
|
77
|
+
participant.isScreenShareEnabled,
|
|
78
|
+
participant.isMicrophoneEnabled,
|
|
79
|
+
participantVideoPublications(participant).map(
|
|
80
|
+
(publication) => `${publication.trackSid}:${publication.source}:${publication.isMuted}:${publication.videoTrack != null}`
|
|
81
|
+
).join(",")
|
|
82
|
+
].join(":")
|
|
83
|
+
).join("|");
|
|
84
|
+
return `${room.state}:${room.remoteParticipants.size}:${room.activeSpeakers.length}:${trackState}`;
|
|
85
|
+
},
|
|
86
|
+
() => ""
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
function useMeetingViewState(controller) {
|
|
90
|
+
const [viewState, setViewState] = (0, import_react.useState)("preview");
|
|
91
|
+
const connectionState = (0, import_react.useSyncExternalStore)(
|
|
92
|
+
(listener) => controller.subscribe(listener),
|
|
93
|
+
() => controller.livekitRoom.state,
|
|
94
|
+
() => controller.livekitRoom.state
|
|
95
|
+
);
|
|
96
|
+
(0, import_react.useEffect)(() => {
|
|
97
|
+
if (connectionState === import_livekit_client.ConnectionState.Disconnected) {
|
|
98
|
+
setViewState("preview");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (connectionState === import_livekit_client.ConnectionState.Connected) {
|
|
102
|
+
setViewState("joined");
|
|
103
|
+
}
|
|
104
|
+
}, [connectionState]);
|
|
105
|
+
const joinMeeting = (0, import_react.useCallback)(
|
|
106
|
+
async (options) => {
|
|
107
|
+
await controller.connect((0, import_lobby.meetingFastConnectOptions)(options));
|
|
108
|
+
if (options.audioOutputDeviceId != null) {
|
|
109
|
+
await controller.livekitRoom.switchActiveDevice("audiooutput", options.audioOutputDeviceId).catch((error) => {
|
|
110
|
+
console.warn("Unable to switch audio output device", error);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
setViewState("joined");
|
|
114
|
+
},
|
|
115
|
+
[controller]
|
|
116
|
+
);
|
|
117
|
+
return { viewState, joinMeeting };
|
|
118
|
+
}
|
|
119
|
+
function participantDisplayName(participant) {
|
|
120
|
+
return participant.name?.trim() || participant.identity || "Participant";
|
|
121
|
+
}
|
|
122
|
+
function participantVideoPublications(participant) {
|
|
123
|
+
return Array.from(
|
|
124
|
+
participant.videoTrackPublications.values()
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
function activeVideoPublicationForSource(participant, source) {
|
|
128
|
+
const publication = participant.getTrackPublication(source);
|
|
129
|
+
if (publication == null || publication.isMuted || publication.videoTrack == null) {
|
|
130
|
+
return void 0;
|
|
131
|
+
}
|
|
132
|
+
return publication;
|
|
133
|
+
}
|
|
134
|
+
function activeVideoPublications(participant, options = {}) {
|
|
135
|
+
return participantVideoPublications(participant).filter(
|
|
136
|
+
(publication) => !publication.isMuted && publication.videoTrack != null && (options.source == null || publication.source === options.source)
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
function screenSharePublications(participants) {
|
|
140
|
+
return participants.flatMap((participant) => {
|
|
141
|
+
const publication = activeVideoPublicationForSource(
|
|
142
|
+
participant,
|
|
143
|
+
import_camera_grid.TrackSource.ScreenShareVideo
|
|
144
|
+
);
|
|
145
|
+
return publication == null ? [] : [{ participant, publication }];
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
function useAttachedVideoTrack(track) {
|
|
149
|
+
const attachedElementRef = (0, import_react.useRef)(null);
|
|
150
|
+
(0, import_react.useEffect)(() => {
|
|
151
|
+
const element = attachedElementRef.current;
|
|
152
|
+
if (element == null || track == null) {
|
|
153
|
+
return void 0;
|
|
154
|
+
}
|
|
155
|
+
track.attach(element);
|
|
156
|
+
return () => {
|
|
157
|
+
track.detach(element);
|
|
158
|
+
};
|
|
159
|
+
}, [track]);
|
|
160
|
+
return (0, import_react.useCallback)((element) => {
|
|
161
|
+
attachedElementRef.current = element;
|
|
162
|
+
}, []);
|
|
163
|
+
}
|
|
164
|
+
function VideoTrackView({
|
|
165
|
+
track,
|
|
166
|
+
fit,
|
|
167
|
+
muted
|
|
168
|
+
}) {
|
|
169
|
+
const videoRef = useAttachedVideoTrack(track);
|
|
170
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
171
|
+
"video",
|
|
172
|
+
{
|
|
173
|
+
ref: videoRef,
|
|
174
|
+
autoPlay: true,
|
|
175
|
+
playsInline: true,
|
|
176
|
+
muted,
|
|
177
|
+
className: (0, import_utils.cn)(
|
|
178
|
+
"h-full w-full bg-[#222222]",
|
|
179
|
+
fit === "contain" ? "object-contain" : "object-cover"
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
function ParticipantFrame({
|
|
185
|
+
args,
|
|
186
|
+
expandedTarget,
|
|
187
|
+
onToggleExpanded,
|
|
188
|
+
strip = false
|
|
189
|
+
}) {
|
|
190
|
+
const [hovered, setHovered] = (0, import_react.useState)(false);
|
|
191
|
+
const { participant, publication, trackNode, showName } = args;
|
|
192
|
+
const source = publication?.source ?? import_camera_grid.TrackSource.Camera;
|
|
193
|
+
const expanded = expandedTarget?.identity === participant.identity && expandedTarget.source === source;
|
|
194
|
+
const muted = !participant.isMicrophoneEnabled;
|
|
195
|
+
const displayName = participantDisplayName(participant);
|
|
196
|
+
const showLabel = showName && (hovered || expanded || strip);
|
|
197
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
198
|
+
"div",
|
|
199
|
+
{
|
|
200
|
+
className: "relative h-full w-full overflow-hidden rounded-md border bg-[#222222]",
|
|
201
|
+
onMouseEnter: () => setHovered(true),
|
|
202
|
+
onMouseLeave: () => setHovered(false),
|
|
203
|
+
children: [
|
|
204
|
+
trackNode,
|
|
205
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "absolute right-1.5 top-1.5 flex max-w-[calc(100%-0.75rem)] items-center rounded-md px-2 py-1.5 text-xs font-medium text-white drop-shadow", children: [
|
|
206
|
+
muted ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.MicOff, { className: "h-4 w-4 shrink-0 text-red-400" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Mic, { className: "h-4 w-4 shrink-0" }),
|
|
207
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
208
|
+
"span",
|
|
209
|
+
{
|
|
210
|
+
className: (0, import_utils.cn)(
|
|
211
|
+
"overflow-hidden whitespace-nowrap transition-[max-width,opacity] duration-200",
|
|
212
|
+
showLabel ? "ml-1 max-w-40 opacity-100" : "max-w-0 opacity-0"
|
|
213
|
+
),
|
|
214
|
+
children: displayName
|
|
215
|
+
}
|
|
216
|
+
),
|
|
217
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
218
|
+
import_button.Button,
|
|
219
|
+
{
|
|
220
|
+
type: "button",
|
|
221
|
+
variant: "ghost",
|
|
222
|
+
size: "icon",
|
|
223
|
+
className: "ml-1 h-5 w-5 shrink-0 text-white hover:bg-white/10 hover:text-white",
|
|
224
|
+
title: expanded ? "Collapse view" : "Expand view",
|
|
225
|
+
"aria-label": expanded ? "Collapse view" : "Expand view",
|
|
226
|
+
onClick: () => onToggleExpanded(participant.identity, source),
|
|
227
|
+
children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Minimize2, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Expand, { className: "h-3.5 w-3.5" })
|
|
228
|
+
}
|
|
229
|
+
)
|
|
230
|
+
] })
|
|
231
|
+
]
|
|
232
|
+
}
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
function ParticipantCameraGrid({
|
|
236
|
+
room,
|
|
237
|
+
participants,
|
|
238
|
+
preferredSource,
|
|
239
|
+
expandedTarget,
|
|
240
|
+
onToggleExpanded
|
|
241
|
+
}) {
|
|
242
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
243
|
+
import_camera_grid.CameraGrid,
|
|
244
|
+
{
|
|
245
|
+
room,
|
|
246
|
+
participants,
|
|
247
|
+
spacing: 12,
|
|
248
|
+
preferredSource,
|
|
249
|
+
activeVideoPublicationForSource,
|
|
250
|
+
activeVideoPublications,
|
|
251
|
+
renderVideoTrack: ({ track, fit, publication }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
252
|
+
VideoTrackView,
|
|
253
|
+
{
|
|
254
|
+
track,
|
|
255
|
+
fit,
|
|
256
|
+
muted: publication.isLocal && publication.source !== import_camera_grid.TrackSource.ScreenShareVideo
|
|
257
|
+
}
|
|
258
|
+
),
|
|
259
|
+
renderAudioStats: ({ participant }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
260
|
+
import_audio_visualization.AudioWave,
|
|
261
|
+
{
|
|
262
|
+
room,
|
|
263
|
+
participant,
|
|
264
|
+
className: "flex h-full w-full items-center justify-center bg-[#222222]"
|
|
265
|
+
}
|
|
266
|
+
),
|
|
267
|
+
frameBuilder: (args) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
268
|
+
ParticipantFrame,
|
|
269
|
+
{
|
|
270
|
+
args,
|
|
271
|
+
expandedTarget,
|
|
272
|
+
onToggleExpanded
|
|
273
|
+
}
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
function CameraStrip({
|
|
279
|
+
room,
|
|
280
|
+
participants,
|
|
281
|
+
horizontal,
|
|
282
|
+
expandedTarget,
|
|
283
|
+
onToggleExpanded
|
|
284
|
+
}) {
|
|
285
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
286
|
+
"div",
|
|
287
|
+
{
|
|
288
|
+
className: (0, import_utils.cn)(
|
|
289
|
+
"flex gap-3 overflow-auto",
|
|
290
|
+
horizontal ? "h-full flex-row" : "h-full flex-col"
|
|
291
|
+
),
|
|
292
|
+
children: participants.map((participant) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
293
|
+
"div",
|
|
294
|
+
{
|
|
295
|
+
className: (0, import_utils.cn)(
|
|
296
|
+
"aspect-video overflow-hidden rounded-md",
|
|
297
|
+
horizontal ? "h-full min-w-44" : "w-full"
|
|
298
|
+
),
|
|
299
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
300
|
+
import_camera_grid.CameraGrid,
|
|
301
|
+
{
|
|
302
|
+
room,
|
|
303
|
+
participants: [participant],
|
|
304
|
+
showNames: true,
|
|
305
|
+
activeVideoPublicationForSource,
|
|
306
|
+
activeVideoPublications,
|
|
307
|
+
preferredSource: import_camera_grid.TrackSource.Camera,
|
|
308
|
+
renderVideoTrack: ({ track, fit, publication }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
309
|
+
VideoTrackView,
|
|
310
|
+
{
|
|
311
|
+
track,
|
|
312
|
+
fit,
|
|
313
|
+
muted: publication.isLocal
|
|
314
|
+
}
|
|
315
|
+
),
|
|
316
|
+
renderAudioStats: ({ participant: participant2 }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
317
|
+
import_audio_visualization.AudioWave,
|
|
318
|
+
{
|
|
319
|
+
room,
|
|
320
|
+
participant: participant2,
|
|
321
|
+
className: "flex h-full w-full items-center justify-center bg-[#222222]"
|
|
322
|
+
}
|
|
323
|
+
),
|
|
324
|
+
frameBuilder: (args) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
325
|
+
ParticipantFrame,
|
|
326
|
+
{
|
|
327
|
+
args,
|
|
328
|
+
expandedTarget,
|
|
329
|
+
onToggleExpanded,
|
|
330
|
+
strip: true
|
|
331
|
+
}
|
|
332
|
+
)
|
|
333
|
+
}
|
|
334
|
+
)
|
|
335
|
+
},
|
|
336
|
+
participant.sid || participant.identity
|
|
337
|
+
))
|
|
338
|
+
}
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
function fitAspect({
|
|
342
|
+
aspectRatio,
|
|
343
|
+
maxWidth,
|
|
344
|
+
maxHeight
|
|
345
|
+
}) {
|
|
346
|
+
if (maxWidth <= 0 || maxHeight <= 0 || aspectRatio <= 0) {
|
|
347
|
+
return { width: 0, height: 0 };
|
|
348
|
+
}
|
|
349
|
+
let width = maxWidth;
|
|
350
|
+
let height = width / aspectRatio;
|
|
351
|
+
if (height > maxHeight) {
|
|
352
|
+
height = maxHeight;
|
|
353
|
+
width = height * aspectRatio;
|
|
354
|
+
}
|
|
355
|
+
return { width, height };
|
|
356
|
+
}
|
|
357
|
+
function shouldPutStripOnLeft({
|
|
358
|
+
width,
|
|
359
|
+
height,
|
|
360
|
+
aspectRatio
|
|
361
|
+
}) {
|
|
362
|
+
const leftFit = fitAspect({
|
|
363
|
+
aspectRatio,
|
|
364
|
+
maxWidth: Math.max(0, width - desktopStripWidth - railGap),
|
|
365
|
+
maxHeight: height
|
|
366
|
+
});
|
|
367
|
+
const topFit = fitAspect({
|
|
368
|
+
aspectRatio,
|
|
369
|
+
maxWidth: width,
|
|
370
|
+
maxHeight: Math.max(0, height - desktopStripHeight - railGap)
|
|
371
|
+
});
|
|
372
|
+
return leftFit.width * leftFit.height >= topFit.width * topFit.height;
|
|
373
|
+
}
|
|
374
|
+
function useElementSize() {
|
|
375
|
+
const [element, setElement] = (0, import_react.useState)(null);
|
|
376
|
+
const [size, setSize] = (0, import_react.useState)({ width: 0, height: 0 });
|
|
377
|
+
(0, import_react.useEffect)(() => {
|
|
378
|
+
if (element == null) {
|
|
379
|
+
return void 0;
|
|
380
|
+
}
|
|
381
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
382
|
+
setSize({
|
|
383
|
+
width: entry.contentRect.width,
|
|
384
|
+
height: entry.contentRect.height
|
|
385
|
+
});
|
|
386
|
+
});
|
|
387
|
+
observer.observe(element);
|
|
388
|
+
return () => observer.disconnect();
|
|
389
|
+
}, [element]);
|
|
390
|
+
return [setElement, size];
|
|
391
|
+
}
|
|
392
|
+
function useMediaQuery(query) {
|
|
393
|
+
const getSnapshot = (0, import_react.useCallback)(() => {
|
|
394
|
+
if (typeof window === "undefined") {
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
return window.matchMedia(query).matches;
|
|
398
|
+
}, [query]);
|
|
399
|
+
return (0, import_react.useSyncExternalStore)(
|
|
400
|
+
(listener) => {
|
|
401
|
+
if (typeof window === "undefined") {
|
|
402
|
+
return () => {
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
const mediaQuery = window.matchMedia(query);
|
|
406
|
+
mediaQuery.addEventListener("change", listener);
|
|
407
|
+
return () => mediaQuery.removeEventListener("change", listener);
|
|
408
|
+
},
|
|
409
|
+
getSnapshot,
|
|
410
|
+
() => false
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
function DesktopShareLayout({
|
|
414
|
+
room,
|
|
415
|
+
participants,
|
|
416
|
+
shares,
|
|
417
|
+
expandedTarget,
|
|
418
|
+
onToggleExpanded
|
|
419
|
+
}) {
|
|
420
|
+
const [containerRef, size] = useElementSize();
|
|
421
|
+
const firstShareDimensions = shares[0]?.publication.dimensions;
|
|
422
|
+
const firstShareAspectRatio = firstShareDimensions != null && firstShareDimensions.height > 0 ? firstShareDimensions.width / firstShareDimensions.height : 16 / 9;
|
|
423
|
+
const stripOnLeft = expandedTarget != null || shares.length !== 1 || shouldPutStripOnLeft({
|
|
424
|
+
width: size.width,
|
|
425
|
+
height: size.height,
|
|
426
|
+
aspectRatio: firstShareAspectRatio
|
|
427
|
+
});
|
|
428
|
+
const gridParticipants = expandedTarget == null ? participants : participants.filter(
|
|
429
|
+
(participant) => participant.identity === expandedTarget.identity
|
|
430
|
+
);
|
|
431
|
+
const grid = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
432
|
+
ParticipantCameraGrid,
|
|
433
|
+
{
|
|
434
|
+
room,
|
|
435
|
+
participants: gridParticipants,
|
|
436
|
+
preferredSource: expandedTarget?.source,
|
|
437
|
+
expandedTarget,
|
|
438
|
+
onToggleExpanded
|
|
439
|
+
}
|
|
440
|
+
);
|
|
441
|
+
if (!stripOnLeft) {
|
|
442
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref: containerRef, className: "flex h-full min-h-0 flex-col gap-4", children: [
|
|
443
|
+
expandedTarget == null ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-[100px] min-h-0", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
444
|
+
CameraStrip,
|
|
445
|
+
{
|
|
446
|
+
room,
|
|
447
|
+
participants,
|
|
448
|
+
horizontal: true,
|
|
449
|
+
expandedTarget,
|
|
450
|
+
onToggleExpanded
|
|
451
|
+
}
|
|
452
|
+
) }) : null,
|
|
453
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "min-h-0 flex-1", children: grid })
|
|
454
|
+
] });
|
|
455
|
+
}
|
|
456
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref: containerRef, className: "flex h-full min-h-0 gap-4", children: [
|
|
457
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "min-h-0 flex-1", children: grid }),
|
|
458
|
+
expandedTarget == null ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "min-h-0 w-[250px]", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
459
|
+
CameraStrip,
|
|
460
|
+
{
|
|
461
|
+
room,
|
|
462
|
+
participants,
|
|
463
|
+
horizontal: false,
|
|
464
|
+
expandedTarget,
|
|
465
|
+
onToggleExpanded
|
|
466
|
+
}
|
|
467
|
+
) }) : null
|
|
468
|
+
] });
|
|
469
|
+
}
|
|
470
|
+
function MobileMeetingLayout({
|
|
471
|
+
room,
|
|
472
|
+
participants,
|
|
473
|
+
hasShare,
|
|
474
|
+
expandedTarget,
|
|
475
|
+
onToggleExpanded
|
|
476
|
+
}) {
|
|
477
|
+
const gridParticipants = expandedTarget == null ? participants : participants.filter(
|
|
478
|
+
(participant) => participant.identity === expandedTarget.identity
|
|
479
|
+
);
|
|
480
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex h-full min-h-0 flex-col gap-3", children: [
|
|
481
|
+
hasShare && expandedTarget == null ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-[100px] min-h-0", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
482
|
+
CameraStrip,
|
|
483
|
+
{
|
|
484
|
+
room,
|
|
485
|
+
participants,
|
|
486
|
+
horizontal: true,
|
|
487
|
+
expandedTarget,
|
|
488
|
+
onToggleExpanded
|
|
489
|
+
}
|
|
490
|
+
) }) : null,
|
|
491
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
492
|
+
ParticipantCameraGrid,
|
|
493
|
+
{
|
|
494
|
+
room,
|
|
495
|
+
participants: gridParticipants,
|
|
496
|
+
preferredSource: expandedTarget?.source,
|
|
497
|
+
expandedTarget,
|
|
498
|
+
onToggleExpanded
|
|
499
|
+
}
|
|
500
|
+
) })
|
|
501
|
+
] });
|
|
502
|
+
}
|
|
503
|
+
function MeetingStage({ controller }) {
|
|
504
|
+
const room = controller.livekitRoom;
|
|
505
|
+
useRoomSnapshot(room);
|
|
506
|
+
const [expandedTarget, setExpandedTarget] = (0, import_react.useState)(null);
|
|
507
|
+
const participants = [
|
|
508
|
+
room.localParticipant,
|
|
509
|
+
...Array.from(room.remoteParticipants.values())
|
|
510
|
+
];
|
|
511
|
+
const shares = screenSharePublications(participants);
|
|
512
|
+
const hasShare = shares.length > 0;
|
|
513
|
+
const isMobile = useMediaQuery("(max-width: 767px)");
|
|
514
|
+
(0, import_react.useEffect)(() => {
|
|
515
|
+
if (expandedTarget == null) {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const participant = participants.find(
|
|
519
|
+
(candidate) => candidate.identity === expandedTarget.identity
|
|
520
|
+
);
|
|
521
|
+
if (participant == null) {
|
|
522
|
+
setExpandedTarget(null);
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
if (expandedTarget.source === import_camera_grid.TrackSource.ScreenShareVideo && activeVideoPublicationForSource(
|
|
526
|
+
participant,
|
|
527
|
+
import_camera_grid.TrackSource.ScreenShareVideo
|
|
528
|
+
) == null) {
|
|
529
|
+
setExpandedTarget(null);
|
|
530
|
+
}
|
|
531
|
+
}, [expandedTarget, participants]);
|
|
532
|
+
const toggleExpanded = (0, import_react.useCallback)((identity, source) => {
|
|
533
|
+
setExpandedTarget(
|
|
534
|
+
(current) => current?.identity === identity && current.source === source ? null : { identity, source }
|
|
535
|
+
);
|
|
536
|
+
}, []);
|
|
537
|
+
if (hasShare) {
|
|
538
|
+
if (isMobile) {
|
|
539
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
540
|
+
MobileMeetingLayout,
|
|
541
|
+
{
|
|
542
|
+
room,
|
|
543
|
+
participants,
|
|
544
|
+
hasShare,
|
|
545
|
+
expandedTarget,
|
|
546
|
+
onToggleExpanded: toggleExpanded
|
|
547
|
+
}
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
551
|
+
DesktopShareLayout,
|
|
552
|
+
{
|
|
553
|
+
room,
|
|
554
|
+
participants,
|
|
555
|
+
shares,
|
|
556
|
+
expandedTarget,
|
|
557
|
+
onToggleExpanded: toggleExpanded
|
|
558
|
+
}
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
const gridParticipants = expandedTarget == null ? participants : participants.filter(
|
|
562
|
+
(participant) => participant.identity === expandedTarget.identity
|
|
563
|
+
);
|
|
564
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
565
|
+
ParticipantCameraGrid,
|
|
566
|
+
{
|
|
567
|
+
room,
|
|
568
|
+
participants: gridParticipants,
|
|
569
|
+
preferredSource: expandedTarget?.source,
|
|
570
|
+
expandedTarget,
|
|
571
|
+
onToggleExpanded: toggleExpanded
|
|
572
|
+
}
|
|
573
|
+
);
|
|
574
|
+
}
|
|
575
|
+
function supportsScreenShare() {
|
|
576
|
+
return typeof navigator !== "undefined" && navigator.mediaDevices?.getDisplayMedia != null;
|
|
577
|
+
}
|
|
578
|
+
function ShareScreenToggle({ controller: providedController }) {
|
|
579
|
+
const controller = (0, import_meeting_scope.useMeetingController)(providedController);
|
|
580
|
+
useRoomSnapshot(controller.livekitRoom);
|
|
581
|
+
const [processing, setProcessing] = (0, import_react.useState)(false);
|
|
582
|
+
const participant = controller.livekitRoom.localParticipant;
|
|
583
|
+
if (participant == null || !supportsScreenShare()) {
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
const sharing = participant.isScreenShareEnabled;
|
|
587
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
588
|
+
import_button.Button,
|
|
589
|
+
{
|
|
590
|
+
type: "button",
|
|
591
|
+
title: sharing ? "Stop sharing screen" : "Share screen",
|
|
592
|
+
"aria-label": sharing ? "Stop sharing screen" : "Share screen",
|
|
593
|
+
variant: sharing ? "default" : "outline",
|
|
594
|
+
size: "icon",
|
|
595
|
+
disabled: processing,
|
|
596
|
+
className: (0, import_utils.cn)(
|
|
597
|
+
"h-12 w-12",
|
|
598
|
+
sharing ? "bg-emerald-600 text-white hover:bg-emerald-700" : null
|
|
599
|
+
),
|
|
600
|
+
onClick: () => {
|
|
601
|
+
setProcessing(true);
|
|
602
|
+
participant.setScreenShareEnabled(!sharing).catch((error) => {
|
|
603
|
+
console.warn("Unable to change screen share state", error);
|
|
604
|
+
}).finally(() => setProcessing(false));
|
|
605
|
+
},
|
|
606
|
+
children: processing ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_spinner.Spinner, { className: "h-5 w-5" }) : sharing ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.MonitorX, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.MonitorUp, {})
|
|
607
|
+
}
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
function ActiveMeetingToolbar({ controller, onDisconnect }) {
|
|
611
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-wrap items-center justify-center gap-2", children: [
|
|
612
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_controls.MeetingControls, { controller, onDisconnect, spacing: 8 }),
|
|
613
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShareScreenToggle, { controller })
|
|
614
|
+
] });
|
|
615
|
+
}
|
|
616
|
+
function MeetingView({ controller: providedController, onCancel }) {
|
|
617
|
+
const controller = (0, import_meeting_scope.useMeetingController)(providedController);
|
|
618
|
+
const { viewState, joinMeeting } = useMeetingViewState(controller);
|
|
619
|
+
const connected = controller.livekitRoom.state !== import_livekit_client.ConnectionState.Disconnected;
|
|
620
|
+
const inPreview = viewState === "preview" || !connected;
|
|
621
|
+
if (inPreview) {
|
|
622
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
623
|
+
import_lobby.MeetingLobby,
|
|
624
|
+
{
|
|
625
|
+
controller,
|
|
626
|
+
onCancel,
|
|
627
|
+
onJoin: joinMeeting
|
|
628
|
+
}
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex h-full min-h-0 flex-col", children: [
|
|
632
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex-0 border-b px-5 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ActiveMeetingToolbar, { controller, onDisconnect: onCancel }) }),
|
|
633
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "min-h-0 flex-1 p-5", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MeetingStage, { controller }) })
|
|
634
|
+
] });
|
|
635
|
+
}
|
|
@@ -16,7 +16,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
16
16
|
var meetings_exports = {};
|
|
17
17
|
module.exports = __toCommonJS(meetings_exports);
|
|
18
18
|
__reExport(meetings_exports, require("./audio-visualization"), module.exports);
|
|
19
|
+
__reExport(meetings_exports, require("./camera-grid"), module.exports);
|
|
19
20
|
__reExport(meetings_exports, require("./controls"), module.exports);
|
|
21
|
+
__reExport(meetings_exports, require("./lobby"), module.exports);
|
|
20
22
|
__reExport(meetings_exports, require("./meeting-scope"), module.exports);
|
|
23
|
+
__reExport(meetings_exports, require("./meeting-view"), module.exports);
|
|
21
24
|
__reExport(meetings_exports, require("./participants"), module.exports);
|
|
22
25
|
__reExport(meetings_exports, require("./wake-lock"), module.exports);
|
|
@@ -40,13 +40,13 @@ function WakeLocker({ children }) {
|
|
|
40
40
|
(0, import_react.useEffect)(() => {
|
|
41
41
|
wakeLockRefCount += 1;
|
|
42
42
|
if (wakeLockRefCount === 1) {
|
|
43
|
-
|
|
43
|
+
enableWakeLock().catch(() => {
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
return () => {
|
|
47
47
|
wakeLockRefCount = Math.max(wakeLockRefCount - 1, 0);
|
|
48
48
|
if (wakeLockRefCount === 0) {
|
|
49
|
-
|
|
49
|
+
disableWakeLock().catch(() => {
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
52
|
};
|