@meshagent/meshagent-tailwind 0.41.4 → 0.41.6

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 +10 -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,595 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import {
3
+ LocalAudioTrack,
4
+ LocalVideoTrack,
5
+ Room
6
+ } from "livekit-client";
7
+ import { Video, VideoOff, Mic, MicOff, Settings } from "lucide-react";
8
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
9
+ import { Button } from "../components/ui/button";
10
+ import {
11
+ Dialog,
12
+ DialogContent,
13
+ DialogDescription,
14
+ DialogFooter,
15
+ DialogHeader,
16
+ DialogTitle,
17
+ DialogTrigger
18
+ } from "../components/ui/dialog";
19
+ import {
20
+ Select,
21
+ SelectContent,
22
+ SelectItem,
23
+ SelectTrigger,
24
+ SelectValue
25
+ } from "../components/ui/select";
26
+ import { Spinner } from "../components/ui/spinner";
27
+ import { cn } from "../lib/utils";
28
+ import {
29
+ useMeetingController
30
+ } from "./meeting-scope";
31
+ const audioInputStorageKey = "audioInput";
32
+ const audioOutputStorageKey = "audioOutput";
33
+ const videoInputStorageKey = "videoInput";
34
+ const minimumLobbyPendingDuration = 350;
35
+ function storedDeviceId(key) {
36
+ if (typeof window === "undefined") {
37
+ return "";
38
+ }
39
+ return window.localStorage.getItem(key) ?? "";
40
+ }
41
+ function storeDeviceId(key, value) {
42
+ if (typeof window === "undefined") {
43
+ return;
44
+ }
45
+ if (value === "") {
46
+ window.localStorage.removeItem(key);
47
+ return;
48
+ }
49
+ window.localStorage.setItem(key, value);
50
+ }
51
+ function deviceLabel(device, fallbackPrefix) {
52
+ const label = device?.label.trim();
53
+ return label != null && label !== "" ? label.replace(/^Default - /u, "") : `Default ${fallbackPrefix}`;
54
+ }
55
+ function devicesForKind(devices, kind) {
56
+ return devices.filter(
57
+ (device) => device.kind === kind && device.deviceId !== ""
58
+ );
59
+ }
60
+ function captureDeviceConstraint(deviceId) {
61
+ return deviceId === "" ? void 0 : { exact: deviceId };
62
+ }
63
+ function videoCaptureOptions(deviceId) {
64
+ const constraint = captureDeviceConstraint(deviceId);
65
+ return constraint == null ? void 0 : { deviceId: constraint };
66
+ }
67
+ function audioCaptureOptions(deviceId) {
68
+ const constraint = captureDeviceConstraint(deviceId);
69
+ return constraint == null ? void 0 : { deviceId: constraint };
70
+ }
71
+ function stopLocalVideoTrack(track) {
72
+ track?.stop();
73
+ }
74
+ function stopLocalAudioTrack(track) {
75
+ track?.stop();
76
+ }
77
+ async function createPreviewVideoTrack(deviceId) {
78
+ const constraints = deviceId === "" ? true : { deviceId: { exact: deviceId } };
79
+ const stream = await navigator.mediaDevices.getUserMedia({
80
+ video: constraints,
81
+ audio: false
82
+ });
83
+ const track = stream.getVideoTracks()[0];
84
+ if (track == null) {
85
+ throw new Error("No video track was created");
86
+ }
87
+ return new LocalVideoTrack(
88
+ track,
89
+ constraints === true ? void 0 : constraints
90
+ );
91
+ }
92
+ async function createPreviewAudioTrack(deviceId) {
93
+ const constraints = deviceId === "" ? true : { deviceId: { exact: deviceId } };
94
+ const stream = await navigator.mediaDevices.getUserMedia({
95
+ video: false,
96
+ audio: constraints
97
+ });
98
+ const track = stream.getAudioTracks()[0];
99
+ if (track == null) {
100
+ throw new Error("No audio track was created");
101
+ }
102
+ return new LocalAudioTrack(
103
+ track,
104
+ constraints === true ? void 0 : constraints
105
+ );
106
+ }
107
+ function useLobbyDevices() {
108
+ const [devices, setDevices] = useState([]);
109
+ const refreshDevices = useCallback(() => {
110
+ void Room.getLocalDevices(void 0, false).then(setDevices).catch(() => setDevices([]));
111
+ }, []);
112
+ useEffect(() => {
113
+ refreshDevices();
114
+ navigator.mediaDevices?.addEventListener?.("devicechange", refreshDevices);
115
+ return () => navigator.mediaDevices?.removeEventListener?.(
116
+ "devicechange",
117
+ refreshDevices
118
+ );
119
+ }, [refreshDevices]);
120
+ return { devices, refreshDevices };
121
+ }
122
+ async function runWithMinimumPendingDuration(action) {
123
+ const startedAt = Date.now();
124
+ await action();
125
+ const remaining = minimumLobbyPendingDuration - (Date.now() - startedAt);
126
+ if (remaining > 0) {
127
+ await new Promise((resolve) => window.setTimeout(resolve, remaining));
128
+ }
129
+ }
130
+ function useMeetingLobbyState() {
131
+ const { devices, refreshDevices } = useLobbyDevices();
132
+ const [loaded, setLoaded] = useState(false);
133
+ const [audioOn, setAudioOn] = useState(true);
134
+ const [videoOn, setVideoOn] = useState(true);
135
+ const [audioProcessing, setAudioProcessing] = useState(false);
136
+ const [videoProcessing, setVideoProcessing] = useState(false);
137
+ const [audioUnavailable, setAudioUnavailable] = useState(false);
138
+ const [videoUnavailable, setVideoUnavailable] = useState(false);
139
+ const [audioDeviceId, setAudioDeviceIdState] = useState(() => storedDeviceId(audioInputStorageKey));
140
+ const [audioOutputDeviceId, setAudioOutputDeviceIdState] = useState(() => storedDeviceId(audioOutputStorageKey));
141
+ const [videoDeviceId, setVideoDeviceIdState] = useState(() => storedDeviceId(videoInputStorageKey));
142
+ const [audioTrack, setAudioTrack] = useState(null);
143
+ const [videoTrack, setVideoTrack] = useState(null);
144
+ const disposedRef = useRef(false);
145
+ const audioTrackRef = useRef(null);
146
+ const videoTrackRef = useRef(null);
147
+ const audioTrackRequestRef = useRef(0);
148
+ const videoTrackRequestRef = useRef(0);
149
+ const audioDeviceIdRef = useRef(audioDeviceId);
150
+ const videoDeviceIdRef = useRef(videoDeviceId);
151
+ const replaceAudioTrackState = useCallback(
152
+ (nextTrack) => {
153
+ const currentTrack = audioTrackRef.current;
154
+ if (currentTrack !== nextTrack) {
155
+ stopLocalAudioTrack(currentTrack);
156
+ }
157
+ if (disposedRef.current) {
158
+ stopLocalAudioTrack(nextTrack);
159
+ audioTrackRef.current = null;
160
+ return;
161
+ }
162
+ audioTrackRef.current = nextTrack;
163
+ setAudioTrack(nextTrack);
164
+ },
165
+ []
166
+ );
167
+ const replaceVideoTrackState = useCallback(
168
+ (nextTrack) => {
169
+ const currentTrack = videoTrackRef.current;
170
+ if (currentTrack !== nextTrack) {
171
+ stopLocalVideoTrack(currentTrack);
172
+ }
173
+ if (disposedRef.current) {
174
+ stopLocalVideoTrack(nextTrack);
175
+ videoTrackRef.current = null;
176
+ return;
177
+ }
178
+ videoTrackRef.current = nextTrack;
179
+ setVideoTrack(nextTrack);
180
+ },
181
+ []
182
+ );
183
+ const replaceAudioTrack = useCallback(
184
+ async (deviceId) => {
185
+ const requestId = audioTrackRequestRef.current + 1;
186
+ audioTrackRequestRef.current = requestId;
187
+ setAudioProcessing(true);
188
+ await runWithMinimumPendingDuration(async () => {
189
+ try {
190
+ const nextTrack = await createPreviewAudioTrack(deviceId);
191
+ if (disposedRef.current || requestId !== audioTrackRequestRef.current) {
192
+ stopLocalAudioTrack(nextTrack);
193
+ return;
194
+ }
195
+ replaceAudioTrackState(nextTrack);
196
+ setAudioUnavailable(false);
197
+ } catch (error) {
198
+ if (!disposedRef.current && requestId === audioTrackRequestRef.current) {
199
+ replaceAudioTrackState(null);
200
+ setAudioUnavailable(true);
201
+ console.warn("Unable to start microphone preview", error);
202
+ }
203
+ }
204
+ });
205
+ if (!disposedRef.current && requestId === audioTrackRequestRef.current) {
206
+ setAudioProcessing(false);
207
+ }
208
+ },
209
+ [replaceAudioTrackState]
210
+ );
211
+ const replaceVideoTrack = useCallback(
212
+ async (deviceId) => {
213
+ const requestId = videoTrackRequestRef.current + 1;
214
+ videoTrackRequestRef.current = requestId;
215
+ setVideoProcessing(true);
216
+ await runWithMinimumPendingDuration(async () => {
217
+ try {
218
+ const nextTrack = await createPreviewVideoTrack(deviceId);
219
+ if (disposedRef.current || requestId !== videoTrackRequestRef.current) {
220
+ stopLocalVideoTrack(nextTrack);
221
+ return;
222
+ }
223
+ replaceVideoTrackState(nextTrack);
224
+ setVideoUnavailable(false);
225
+ } catch (error) {
226
+ if (!disposedRef.current && requestId === videoTrackRequestRef.current) {
227
+ replaceVideoTrackState(null);
228
+ setVideoUnavailable(true);
229
+ console.warn("Unable to start camera preview", error);
230
+ }
231
+ }
232
+ });
233
+ if (!disposedRef.current && requestId === videoTrackRequestRef.current) {
234
+ setVideoProcessing(false);
235
+ }
236
+ },
237
+ [replaceVideoTrackState]
238
+ );
239
+ useEffect(() => {
240
+ audioDeviceIdRef.current = audioDeviceId;
241
+ }, [audioDeviceId]);
242
+ useEffect(() => {
243
+ videoDeviceIdRef.current = videoDeviceId;
244
+ }, [videoDeviceId]);
245
+ useEffect(() => {
246
+ let cancelled = false;
247
+ disposedRef.current = false;
248
+ void Promise.all([
249
+ replaceAudioTrack(audioDeviceIdRef.current),
250
+ replaceVideoTrack(videoDeviceIdRef.current)
251
+ ]).finally(() => {
252
+ if (!cancelled) {
253
+ setLoaded(true);
254
+ refreshDevices();
255
+ }
256
+ });
257
+ return () => {
258
+ cancelled = true;
259
+ disposedRef.current = true;
260
+ audioTrackRequestRef.current += 1;
261
+ videoTrackRequestRef.current += 1;
262
+ replaceAudioTrackState(null);
263
+ replaceVideoTrackState(null);
264
+ };
265
+ }, [
266
+ refreshDevices,
267
+ replaceAudioTrack,
268
+ replaceAudioTrackState,
269
+ replaceVideoTrack,
270
+ replaceVideoTrackState
271
+ ]);
272
+ const setAudioDeviceId = useCallback(
273
+ (deviceId) => {
274
+ setAudioDeviceIdState(deviceId);
275
+ storeDeviceId(audioInputStorageKey, deviceId);
276
+ void replaceAudioTrack(deviceId);
277
+ },
278
+ [replaceAudioTrack]
279
+ );
280
+ const setAudioOutputDeviceId = useCallback((deviceId) => {
281
+ setAudioOutputDeviceIdState(deviceId);
282
+ storeDeviceId(audioOutputStorageKey, deviceId);
283
+ }, []);
284
+ const setVideoDeviceId = useCallback(
285
+ (deviceId) => {
286
+ setVideoDeviceIdState(deviceId);
287
+ storeDeviceId(videoInputStorageKey, deviceId);
288
+ void replaceVideoTrack(deviceId);
289
+ },
290
+ [replaceVideoTrack]
291
+ );
292
+ const toggleAudio = useCallback(() => {
293
+ if (audioProcessing) {
294
+ return;
295
+ }
296
+ if (audioTrack != null) {
297
+ replaceAudioTrackState(null);
298
+ setAudioOn(false);
299
+ return;
300
+ }
301
+ setAudioOn(true);
302
+ void replaceAudioTrack(audioDeviceId);
303
+ }, [
304
+ audioDeviceId,
305
+ audioProcessing,
306
+ audioTrack,
307
+ replaceAudioTrack,
308
+ replaceAudioTrackState
309
+ ]);
310
+ const toggleVideo = useCallback(() => {
311
+ if (videoProcessing) {
312
+ return;
313
+ }
314
+ if (videoTrack != null) {
315
+ replaceVideoTrackState(null);
316
+ setVideoOn(false);
317
+ return;
318
+ }
319
+ setVideoOn(true);
320
+ void replaceVideoTrack(videoDeviceId);
321
+ }, [
322
+ replaceVideoTrack,
323
+ replaceVideoTrackState,
324
+ videoDeviceId,
325
+ videoProcessing,
326
+ videoTrack
327
+ ]);
328
+ return {
329
+ loaded,
330
+ videoTrack,
331
+ audioTrack,
332
+ audioOn,
333
+ videoOn,
334
+ audioProcessing,
335
+ videoProcessing,
336
+ audioUnavailable,
337
+ videoUnavailable,
338
+ audioDeviceId,
339
+ audioOutputDeviceId,
340
+ videoDeviceId,
341
+ devices,
342
+ setAudioDeviceId,
343
+ setAudioOutputDeviceId,
344
+ setVideoDeviceId,
345
+ refreshDevices,
346
+ toggleAudio,
347
+ toggleVideo
348
+ };
349
+ }
350
+ function useAttachedPreviewVideo(track) {
351
+ const attachedElementRef = useRef(null);
352
+ useEffect(() => {
353
+ const element = attachedElementRef.current;
354
+ if (element == null || track == null) {
355
+ return void 0;
356
+ }
357
+ track.attach(element);
358
+ return () => {
359
+ track.detach(element);
360
+ };
361
+ }, [track]);
362
+ return useCallback((element) => {
363
+ attachedElementRef.current = element;
364
+ }, []);
365
+ }
366
+ function LobbyDeviceSelect({
367
+ label,
368
+ devices,
369
+ kind,
370
+ value,
371
+ onValueChange
372
+ }) {
373
+ const options = devicesForKind(devices, kind);
374
+ const selectedValue = value === "" ? "default" : value;
375
+ return /* @__PURE__ */ jsxs("div", { className: "grid gap-2", children: [
376
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-medium", children: label }),
377
+ /* @__PURE__ */ jsxs(
378
+ Select,
379
+ {
380
+ value: selectedValue,
381
+ onValueChange: (nextValue) => onValueChange(nextValue === "default" ? "" : nextValue),
382
+ children: [
383
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "w-full", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: deviceLabel(options[0] ?? null, label) }) }),
384
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
385
+ /* @__PURE__ */ jsxs(SelectItem, { value: "default", children: [
386
+ "Default ",
387
+ label.toLowerCase()
388
+ ] }),
389
+ options.map((device) => /* @__PURE__ */ jsx(SelectItem, { value: device.deviceId, children: deviceLabel(device, label) }, device.deviceId))
390
+ ] })
391
+ ]
392
+ }
393
+ )
394
+ ] });
395
+ }
396
+ function LobbyDeviceSettings({ state }) {
397
+ const hasAudioOutput = useMemo(
398
+ () => state.devices.some((device) => device.kind === "audiooutput"),
399
+ [state.devices]
400
+ );
401
+ return /* @__PURE__ */ jsxs(Dialog, { children: [
402
+ /* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
403
+ Button,
404
+ {
405
+ type: "button",
406
+ title: "Device settings",
407
+ "aria-label": "Device settings",
408
+ variant: "outline",
409
+ className: "h-10",
410
+ children: [
411
+ /* @__PURE__ */ jsx(Settings, {}),
412
+ "Device settings"
413
+ ]
414
+ }
415
+ ) }),
416
+ /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-[min(92vw,560px)]", children: [
417
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
418
+ /* @__PURE__ */ jsx(DialogTitle, { children: "Device settings" }),
419
+ /* @__PURE__ */ jsx(DialogDescription, { children: "Choose the devices used for this meeting." })
420
+ ] }),
421
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-4", children: [
422
+ /* @__PURE__ */ jsx(
423
+ LobbyDeviceSelect,
424
+ {
425
+ label: "Camera",
426
+ devices: state.devices,
427
+ kind: "videoinput",
428
+ value: state.videoDeviceId,
429
+ onValueChange: state.setVideoDeviceId
430
+ }
431
+ ),
432
+ /* @__PURE__ */ jsx(
433
+ LobbyDeviceSelect,
434
+ {
435
+ label: "Microphone",
436
+ devices: state.devices,
437
+ kind: "audioinput",
438
+ value: state.audioDeviceId,
439
+ onValueChange: state.setAudioDeviceId
440
+ }
441
+ ),
442
+ hasAudioOutput && /* @__PURE__ */ jsx(
443
+ LobbyDeviceSelect,
444
+ {
445
+ label: "Speaker",
446
+ devices: state.devices,
447
+ kind: "audiooutput",
448
+ value: state.audioOutputDeviceId,
449
+ onValueChange: state.setAudioOutputDeviceId
450
+ }
451
+ )
452
+ ] }),
453
+ /* @__PURE__ */ jsx(DialogFooter, { children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: state.refreshDevices, children: "Refresh" }) })
454
+ ] })
455
+ ] });
456
+ }
457
+ function LobbyToggleButton({
458
+ text,
459
+ on,
460
+ unavailable,
461
+ loading,
462
+ icon,
463
+ offIcon,
464
+ onClick
465
+ }) {
466
+ return /* @__PURE__ */ jsx(
467
+ Button,
468
+ {
469
+ type: "button",
470
+ title: text,
471
+ "aria-label": text,
472
+ size: "icon",
473
+ variant: unavailable ? "destructive" : on ? "default" : "outline",
474
+ disabled: loading,
475
+ onClick,
476
+ className: cn(
477
+ "h-10 w-10",
478
+ on && !unavailable ? "bg-emerald-600 text-white hover:bg-emerald-700" : null
479
+ ),
480
+ children: loading ? /* @__PURE__ */ jsx(Spinner, { className: "h-5 w-5" }) : on ? icon : offIcon
481
+ }
482
+ );
483
+ }
484
+ function joinOptions(state) {
485
+ const enableVideo = state.videoTrack != null && !state.videoUnavailable;
486
+ const enableAudio = state.audioTrack != null && !state.audioUnavailable;
487
+ return {
488
+ enableVideo,
489
+ enableAudio,
490
+ videoUnavailable: state.videoUnavailable,
491
+ audioUnavailable: state.audioUnavailable,
492
+ videoDeviceId: state.videoDeviceId === "" ? void 0 : state.videoDeviceId,
493
+ audioDeviceId: state.audioDeviceId === "" ? void 0 : state.audioDeviceId,
494
+ audioOutputDeviceId: state.audioOutputDeviceId === "" ? void 0 : state.audioOutputDeviceId
495
+ };
496
+ }
497
+ function meetingFastConnectOptions(options) {
498
+ return {
499
+ camera: {
500
+ enabled: options.enableVideo,
501
+ options: videoCaptureOptions(options.videoDeviceId ?? "")
502
+ },
503
+ microphone: {
504
+ enabled: options.enableAudio,
505
+ options: audioCaptureOptions(options.audioDeviceId ?? "")
506
+ }
507
+ };
508
+ }
509
+ function MeetingLobby({
510
+ controller: providedController,
511
+ onCancel,
512
+ onJoin
513
+ }) {
514
+ const controller = useMeetingController(providedController);
515
+ const state = useMeetingLobbyState();
516
+ const previewRef = useAttachedPreviewVideo(state.videoTrack);
517
+ const videoPending = state.videoOn && state.videoTrack == null && !state.videoUnavailable;
518
+ const audioPending = state.audioOn && state.audioTrack == null && !state.audioUnavailable;
519
+ const starting = videoPending || audioPending || state.videoProcessing || state.audioProcessing;
520
+ const canJoin = controller.config != null && !starting;
521
+ const statusText = state.loaded ? "Get ready to meet" : "Preparing devices";
522
+ return /* @__PURE__ */ jsx("div", { className: "flex h-full min-h-0 flex-col px-6 py-5", children: /* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1 items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "grid w-full max-w-[800px] gap-5", children: [
523
+ /* @__PURE__ */ jsx("div", { className: "text-center text-base font-semibold", children: statusText }),
524
+ /* @__PURE__ */ jsx("div", { className: "aspect-[3/2] overflow-hidden rounded-md bg-[#222]", children: state.videoTrack != null ? /* @__PURE__ */ jsx(
525
+ "video",
526
+ {
527
+ ref: previewRef,
528
+ autoPlay: true,
529
+ muted: true,
530
+ playsInline: true,
531
+ className: "h-full w-full object-cover"
532
+ }
533
+ ) : /* @__PURE__ */ jsx("div", { className: "flex h-full w-full items-center justify-center text-sm font-medium text-white/70", children: "Camera off" }) }),
534
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between gap-2", children: [
535
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
536
+ /* @__PURE__ */ jsx(
537
+ LobbyToggleButton,
538
+ {
539
+ text: audioPending ? "Starting microphone" : state.audioTrack != null ? "Turn off microphone" : "Turn on microphone",
540
+ on: state.audioTrack != null || audioPending,
541
+ unavailable: state.audioUnavailable,
542
+ loading: state.audioProcessing || audioPending,
543
+ icon: /* @__PURE__ */ jsx(Mic, {}),
544
+ offIcon: /* @__PURE__ */ jsx(MicOff, {}),
545
+ onClick: state.toggleAudio
546
+ }
547
+ ),
548
+ /* @__PURE__ */ jsx(
549
+ LobbyToggleButton,
550
+ {
551
+ text: videoPending ? "Starting camera" : state.videoTrack != null ? "Turn off camera" : "Turn on camera",
552
+ on: state.videoTrack != null || videoPending,
553
+ unavailable: state.videoUnavailable,
554
+ loading: state.videoProcessing || videoPending,
555
+ icon: /* @__PURE__ */ jsx(Video, {}),
556
+ offIcon: /* @__PURE__ */ jsx(VideoOff, {}),
557
+ onClick: state.toggleVideo
558
+ }
559
+ ),
560
+ /* @__PURE__ */ jsx(LobbyDeviceSettings, { state })
561
+ ] }),
562
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
563
+ onCancel != null && /* @__PURE__ */ jsx(
564
+ Button,
565
+ {
566
+ type: "button",
567
+ variant: "outline",
568
+ className: "h-10 sm:w-[120px]",
569
+ onClick: onCancel,
570
+ children: "Cancel"
571
+ }
572
+ ),
573
+ /* @__PURE__ */ jsx(
574
+ Button,
575
+ {
576
+ type: "button",
577
+ className: "h-10 bg-emerald-600 text-white hover:bg-emerald-700 sm:w-[120px]",
578
+ disabled: !canJoin,
579
+ onClick: () => {
580
+ onJoin?.(joinOptions(state));
581
+ },
582
+ children: starting ? /* @__PURE__ */ jsxs(Fragment, { children: [
583
+ /* @__PURE__ */ jsx(Spinner, { className: "h-4 w-4" }),
584
+ "Starting"
585
+ ] }) : "Meet now"
586
+ }
587
+ )
588
+ ] })
589
+ ] })
590
+ ] }) }) });
591
+ }
592
+ export {
593
+ MeetingLobby,
594
+ meetingFastConnectOptions
595
+ };
@@ -1,17 +1,18 @@
1
1
  import type { ReactElement, ReactNode } from "react";
2
- import { RoomClient } from "@meshagent/meshagent";
3
- import { Room, type LocalParticipant, type Participant, type RoomConnectOptions, type RoomOptions, type TrackPublication } from "livekit-client";
2
+ import type { RoomClient } from "@meshagent/meshagent";
3
+ import "@meshagent/meshagent-react";
4
+ import type { LivekitConnectionInfo } from "@meshagent/meshagent-react";
5
+ import { Room } from "livekit-client";
6
+ import type { AudioCaptureOptions, LocalParticipant, Participant, RoomConnectOptions, RoomOptions, TrackPublication, VideoCaptureOptions } from "livekit-client";
4
7
  type Listener = () => void;
5
- export interface LivekitConnectionInfo {
6
- url: string;
7
- token: string;
8
- }
9
8
  export type MeetingFastConnectOptions = RoomConnectOptions & {
10
9
  camera?: {
11
10
  enabled?: boolean;
11
+ options?: VideoCaptureOptions;
12
12
  };
13
13
  microphone?: {
14
14
  enabled?: boolean;
15
+ options?: AudioCaptureOptions;
15
16
  };
16
17
  };
17
18
  export declare class PendingLocalMediaState {