@meshagent/meshagent-tailwind 0.41.4 → 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.
Files changed (48) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/README.md +33 -0
  3. package/dist/cjs/chat/chat-thread.js +15 -10
  4. package/dist/cjs/chat/dataset-chat-thread.d.ts +4 -1
  5. package/dist/cjs/chat/dataset-chat-thread.js +130 -157
  6. package/dist/cjs/chat/multi-thread-view.d.ts +4 -1
  7. package/dist/cjs/chat/multi-thread-view.js +4 -0
  8. package/dist/cjs/chat/new-chat-thread.d.ts +4 -1
  9. package/dist/cjs/chat/new-chat-thread.js +43 -87
  10. package/dist/cjs/file-preview/file-preview.d.ts +6 -0
  11. package/dist/cjs/file-preview/file-preview.js +220 -0
  12. package/dist/cjs/meetings/camera-grid.d.ts +46 -0
  13. package/dist/cjs/meetings/camera-grid.js +435 -0
  14. package/dist/cjs/meetings/controls.d.ts +4 -2
  15. package/dist/cjs/meetings/controls.js +9 -3
  16. package/dist/cjs/meetings/lobby.d.ts +17 -0
  17. package/dist/cjs/meetings/lobby.js +595 -0
  18. package/dist/cjs/meetings/meeting-scope.d.ts +7 -6
  19. package/dist/cjs/meetings/meeting-scope.js +64 -15
  20. package/dist/cjs/meetings/meeting-view.d.ts +6 -0
  21. package/dist/cjs/meetings/meeting-view.js +635 -0
  22. package/dist/cjs/meetings/meetings.d.ts +3 -0
  23. package/dist/cjs/meetings/meetings.js +3 -0
  24. package/dist/cjs/meetings/wake-lock.js +2 -2
  25. package/dist/esm/chat/chat-thread.js +15 -10
  26. package/dist/esm/chat/dataset-chat-thread.d.ts +4 -1
  27. package/dist/esm/chat/dataset-chat-thread.js +129 -133
  28. package/dist/esm/chat/multi-thread-view.d.ts +4 -1
  29. package/dist/esm/chat/multi-thread-view.js +4 -0
  30. package/dist/esm/chat/new-chat-thread.d.ts +4 -1
  31. package/dist/esm/chat/new-chat-thread.js +43 -87
  32. package/dist/esm/file-preview/file-preview.d.ts +6 -0
  33. package/dist/esm/file-preview/file-preview.js +220 -0
  34. package/dist/esm/meetings/camera-grid.d.ts +46 -0
  35. package/dist/esm/meetings/camera-grid.js +405 -0
  36. package/dist/esm/meetings/controls.d.ts +4 -2
  37. package/dist/esm/meetings/controls.js +9 -3
  38. package/dist/esm/meetings/lobby.d.ts +17 -0
  39. package/dist/esm/meetings/lobby.js +595 -0
  40. package/dist/esm/meetings/meeting-scope.d.ts +7 -6
  41. package/dist/esm/meetings/meeting-scope.js +71 -16
  42. package/dist/esm/meetings/meeting-view.d.ts +6 -0
  43. package/dist/esm/meetings/meeting-view.js +630 -0
  44. package/dist/esm/meetings/meetings.d.ts +3 -0
  45. package/dist/esm/meetings/meetings.js +3 -0
  46. package/dist/esm/meetings/wake-lock.js +2 -2
  47. package/dist/index.css +1 -1
  48. 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
+ }
@@ -1,5 +1,8 @@
1
1
  export * from "./audio-visualization";
2
+ export * from "./camera-grid";
2
3
  export * from "./controls";
4
+ export * from "./lobby";
3
5
  export * from "./meeting-scope";
6
+ export * from "./meeting-view";
4
7
  export * from "./participants";
5
8
  export * from "./wake-lock";
@@ -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
- void enableWakeLock().catch(() => {
43
+ enableWakeLock().catch(() => {
44
44
  });
45
45
  }
46
46
  return () => {
47
47
  wakeLockRefCount = Math.max(wakeLockRefCount - 1, 0);
48
48
  if (wakeLockRefCount === 0) {
49
- void disableWakeLock().catch(() => {
49
+ disableWakeLock().catch(() => {
50
50
  });
51
51
  }
52
52
  };