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