@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,435 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var camera_grid_exports = {};
30
+ __export(camera_grid_exports, {
31
+ CameraGrid: () => CameraGrid,
32
+ TrackSource: () => TrackSource
33
+ });
34
+ module.exports = __toCommonJS(camera_grid_exports);
35
+ var import_jsx_runtime = require("react/jsx-runtime");
36
+ var import_react = __toESM(require("react"));
37
+ var import_livekit_client = require("livekit-client");
38
+ const TrackSource = {
39
+ Camera: import_livekit_client.Track.Source.Camera,
40
+ ScreenShareVideo: import_livekit_client.Track.Source.ScreenShare
41
+ };
42
+ function useParticipantsSnapshot(participants) {
43
+ (0, import_react.useSyncExternalStore)(
44
+ (listener) => {
45
+ const events = [
46
+ "trackPublished",
47
+ "trackSubscribed",
48
+ "trackUnpublished",
49
+ "trackUnsubscribed",
50
+ "trackMuted",
51
+ "trackUnmuted",
52
+ "localTrackPublished",
53
+ "localTrackUnpublished",
54
+ "participantNameChanged",
55
+ "isSpeakingChanged",
56
+ "attributesChanged"
57
+ ];
58
+ for (const participant of participants) {
59
+ for (const eventName of events) {
60
+ participant.on(eventName, listener);
61
+ }
62
+ }
63
+ return () => {
64
+ for (const participant of participants) {
65
+ for (const eventName of events) {
66
+ participant.off(eventName, listener);
67
+ }
68
+ }
69
+ };
70
+ },
71
+ () => participants.map(
72
+ (participant) => [
73
+ participant.sid,
74
+ participant.identity,
75
+ participant.isCameraEnabled,
76
+ participant.isScreenShareEnabled,
77
+ participant.isMicrophoneEnabled,
78
+ participant.isSpeaking,
79
+ participant.trackPublications.size,
80
+ participant.name ?? ""
81
+ ].join(":")
82
+ ).join("|"),
83
+ () => ""
84
+ );
85
+ }
86
+ function publicationAspectRatio(publication) {
87
+ const dimensions = publication?.dimensions ?? { width: 640, height: 480 };
88
+ return dimensions.height > 0 ? dimensions.width / dimensions.height : 4 / 3;
89
+ }
90
+ function layoutCameras(publications, width, height) {
91
+ const slots = Math.max(publications.length, 1);
92
+ let bestRows = 1;
93
+ let bestCols = slots;
94
+ let minWaste = Number.POSITIVE_INFINITY;
95
+ for (let rows = 1; rows <= slots; rows += 1) {
96
+ const cols = Math.ceil(slots / rows);
97
+ const gridAspectRatio = width / cols / (height / rows);
98
+ let aspectRatioWaste = 0;
99
+ for (const publication of publications) {
100
+ aspectRatioWaste += Math.abs(
101
+ publicationAspectRatio(publication) - gridAspectRatio
102
+ );
103
+ }
104
+ if (aspectRatioWaste < minWaste) {
105
+ minWaste = aspectRatioWaste;
106
+ bestRows = rows;
107
+ bestCols = cols;
108
+ }
109
+ }
110
+ return [bestRows, bestCols];
111
+ }
112
+ function CameraGrid({
113
+ participants,
114
+ room,
115
+ spacing = 0,
116
+ showNames = true,
117
+ showAllVideos = false,
118
+ preferredSource,
119
+ rowsDesired = 0,
120
+ columnsDesired = 0,
121
+ tryFill = true,
122
+ activeVideoPublicationForSource,
123
+ activeVideoPublications,
124
+ renderVideoTrack,
125
+ renderAudioStats,
126
+ frameBuilder
127
+ }) {
128
+ useParticipantsSnapshot(participants);
129
+ const items = (0, import_react.useMemo)(() => {
130
+ if (!room) return [];
131
+ const hasShare = participants.some(
132
+ (participant) => activeVideoPublicationForSource(
133
+ participant,
134
+ TrackSource.ScreenShareVideo
135
+ ) != null
136
+ );
137
+ const videoSource = preferredSource ?? (hasShare ? TrackSource.ScreenShareVideo : TrackSource.Camera);
138
+ const shouldShowCameraPlaceholder = videoSource === TrackSource.Camera;
139
+ const nextItems = [];
140
+ for (const participant of participants) {
141
+ let added = false;
142
+ const publications = showAllVideos ? activeVideoPublications(participant) : activeVideoPublications(participant, { source: videoSource });
143
+ for (const publication of publications) {
144
+ const track = publication.videoTrack;
145
+ if (track == null) continue;
146
+ added = true;
147
+ nextItems.push({
148
+ participant,
149
+ publication,
150
+ trackNode: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { pointerEvents: "none", width: "100%", height: "100%" }, children: renderVideoTrack({
151
+ track,
152
+ fit: publication.source === TrackSource.ScreenShareVideo ? "contain" : "cover",
153
+ publication,
154
+ participant
155
+ }) })
156
+ });
157
+ }
158
+ if (shouldShowCameraPlaceholder && !added) {
159
+ nextItems.push({
160
+ participant,
161
+ publication: null,
162
+ trackNode: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
163
+ "div",
164
+ {
165
+ style: {
166
+ width: "100%",
167
+ height: "100%",
168
+ background: "#222222",
169
+ display: "flex",
170
+ alignItems: "center",
171
+ justifyContent: "center"
172
+ },
173
+ children: participant.identity.includes(".agent") && renderAudioStats ? renderAudioStats({ room, participant }) : null
174
+ }
175
+ )
176
+ });
177
+ }
178
+ }
179
+ return nextItems;
180
+ }, [
181
+ activeVideoPublicationForSource,
182
+ activeVideoPublications,
183
+ participants,
184
+ preferredSource,
185
+ renderAudioStats,
186
+ renderVideoTrack,
187
+ room,
188
+ showAllVideos
189
+ ]);
190
+ if (!room || items.length === 0) return null;
191
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { width: "100%", height: "100%", position: "relative" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
192
+ CameraGridLayout,
193
+ {
194
+ items,
195
+ spacing,
196
+ showNames,
197
+ rowsDesired,
198
+ columnsDesired,
199
+ tryFill,
200
+ frameBuilder
201
+ }
202
+ ) });
203
+ }
204
+ function CameraGridLayout({
205
+ items,
206
+ spacing,
207
+ showNames,
208
+ rowsDesired,
209
+ columnsDesired,
210
+ tryFill,
211
+ frameBuilder
212
+ }) {
213
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { position: "absolute", inset: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
214
+ ResponsiveAbsoluteLayout,
215
+ {
216
+ items,
217
+ spacing,
218
+ showNames,
219
+ rowsDesired,
220
+ columnsDesired,
221
+ tryFill,
222
+ frameBuilder
223
+ }
224
+ ) });
225
+ }
226
+ function ResponsiveAbsoluteLayout(props) {
227
+ const { items } = props;
228
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SizeObserver, { children: ({ width, height }) => {
229
+ if (width <= 0 || height <= 0) return null;
230
+ return renderAbsoluteItems({ ...props, width, height, slots: items.length });
231
+ } });
232
+ }
233
+ function renderAbsoluteItems({
234
+ items,
235
+ spacing,
236
+ showNames,
237
+ rowsDesired,
238
+ columnsDesired,
239
+ tryFill,
240
+ frameBuilder,
241
+ width,
242
+ height,
243
+ slots
244
+ }) {
245
+ const positioned = [];
246
+ if (rowsDesired === 0 && columnsDesired === 0) {
247
+ const [rows, cols] = layoutCameras(
248
+ items.map((item) => item.publication),
249
+ width,
250
+ height
251
+ );
252
+ const availableWidth = Math.max(width - spacing * (cols - 1), 0);
253
+ const availableHeight = Math.max(height - spacing * (rows - 1), 0);
254
+ const cellWidth = availableWidth / cols;
255
+ const cellHeight = availableHeight / rows;
256
+ for (let row = 0; row < rows; row += 1) {
257
+ for (let col = 0; col < cols; col += 1) {
258
+ const index = row * cols + col;
259
+ const item = items[index];
260
+ if (!item) break;
261
+ positioned.push(
262
+ absoluteCell({
263
+ key: index,
264
+ left: col * (cellWidth + spacing),
265
+ top: row * (cellHeight + spacing),
266
+ width: cellWidth,
267
+ height: cellHeight,
268
+ item,
269
+ showNames,
270
+ frameBuilder
271
+ })
272
+ );
273
+ }
274
+ }
275
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: positioned });
276
+ }
277
+ let x = 0;
278
+ let y = 0;
279
+ if (rowsDesired > 0 || columnsDesired > 0 || Math.min(width / height, height / width) > 0.5 || slots < 4 && tryFill) {
280
+ let rows;
281
+ let cols;
282
+ if (width < height) {
283
+ rows = Math.trunc(
284
+ rowsDesired > 0 ? rowsDesired : columnsDesired > 0 ? slots / columnsDesired : Math.ceil(Math.sqrt(slots))
285
+ );
286
+ cols = columnsDesired > 0 ? columnsDesired : Math.ceil(slots / rows);
287
+ } else {
288
+ cols = Math.trunc(
289
+ columnsDesired > 0 ? columnsDesired : rowsDesired > 0 ? slots / rowsDesired : Math.ceil(Math.sqrt(slots))
290
+ );
291
+ rows = rowsDesired > 0 ? rowsDesired : Math.ceil(slots / cols);
292
+ }
293
+ const cellWidth = width / cols + 1 - spacing * (cols - 1) / cols;
294
+ const cellHeight = height / rows - spacing * (rows - 1) / rows;
295
+ for (let row = 0; row < rows; row += 1) {
296
+ for (let col = 0; col < cols; col += 1) {
297
+ const index = col + row * cols;
298
+ const item = items[index];
299
+ if (!item) continue;
300
+ positioned.push(
301
+ absoluteCell({
302
+ key: index,
303
+ left: col * cellWidth + spacing * col,
304
+ top: row * cellHeight + spacing * row,
305
+ width: cellWidth,
306
+ height: cellHeight,
307
+ item,
308
+ showNames,
309
+ frameBuilder
310
+ })
311
+ );
312
+ }
313
+ }
314
+ } else {
315
+ const totalSpace = width * height;
316
+ let rowUsedSpace = totalSpace;
317
+ let rows = 1;
318
+ let vertRows = false;
319
+ for (let i = 1; i < 10; i += 0.1) {
320
+ const floored = Math.floor(i);
321
+ const itemSize = height / i;
322
+ const usedSpace = itemSize * itemSize * Math.max(slots, 1);
323
+ if (itemSize * Math.ceil(slots / floored) <= width && itemSize * floored <= height && usedSpace <= totalSpace && totalSpace - usedSpace < rowUsedSpace) {
324
+ rows = i;
325
+ rowUsedSpace = totalSpace - usedSpace;
326
+ vertRows = true;
327
+ }
328
+ }
329
+ for (let i = 1; i < 10; i += 0.1) {
330
+ const floored = Math.floor(i);
331
+ const itemSize = width / i;
332
+ const usedSpace = itemSize * itemSize * Math.max(slots, 1);
333
+ if (itemSize * Math.ceil(slots / floored) <= height && itemSize * floored <= width && usedSpace <= totalSpace && totalSpace - usedSpace < rowUsedSpace) {
334
+ rows = i;
335
+ rowUsedSpace = totalSpace - usedSpace;
336
+ vertRows = false;
337
+ }
338
+ }
339
+ if (vertRows) {
340
+ const itemSize = (height - spacing * rows) / rows;
341
+ for (let index = 0; index < items.length; index += 1) {
342
+ const item = items[index];
343
+ positioned.push(
344
+ absoluteCell({
345
+ key: index,
346
+ left: x,
347
+ top: y,
348
+ width: itemSize,
349
+ height: itemSize,
350
+ item,
351
+ showNames,
352
+ frameBuilder
353
+ })
354
+ );
355
+ x += itemSize + spacing;
356
+ if (x + itemSize > width) {
357
+ x = spacing;
358
+ y += itemSize + spacing;
359
+ }
360
+ }
361
+ } else {
362
+ const itemSize = (width - spacing * rows) / rows;
363
+ for (let index = 0; index < items.length; index += 1) {
364
+ const item = items[index];
365
+ positioned.push(
366
+ absoluteCell({
367
+ key: index,
368
+ left: x,
369
+ top: y,
370
+ width: itemSize,
371
+ height: itemSize,
372
+ item,
373
+ showNames,
374
+ frameBuilder
375
+ })
376
+ );
377
+ y += itemSize + spacing;
378
+ if (y + itemSize > height) {
379
+ y = spacing;
380
+ x += itemSize + spacing;
381
+ }
382
+ }
383
+ }
384
+ }
385
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: positioned });
386
+ }
387
+ function absoluteCell({
388
+ key,
389
+ left,
390
+ top,
391
+ width,
392
+ height,
393
+ item,
394
+ showNames,
395
+ frameBuilder
396
+ }) {
397
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
398
+ "div",
399
+ {
400
+ style: {
401
+ position: "absolute",
402
+ left,
403
+ top,
404
+ width,
405
+ height,
406
+ overflow: "hidden"
407
+ },
408
+ children: frameBuilder({
409
+ participant: item.participant,
410
+ publication: item.publication,
411
+ trackNode: item.trackNode,
412
+ showName: showNames
413
+ })
414
+ },
415
+ key
416
+ );
417
+ }
418
+ function SizeObserver({ children }) {
419
+ const ref = import_react.default.useRef(null);
420
+ const [size, setSize] = import_react.default.useState({
421
+ width: 0,
422
+ height: 0
423
+ });
424
+ import_react.default.useLayoutEffect(() => {
425
+ const element = ref.current;
426
+ if (!element) return;
427
+ const observer = new ResizeObserver(([entry]) => {
428
+ const rect = entry.contentRect;
429
+ setSize({ width: rect.width, height: rect.height });
430
+ });
431
+ observer.observe(element);
432
+ return () => observer.disconnect();
433
+ }, []);
434
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref, className: "absolute inset-0", children: children(size) });
435
+ }
@@ -1,8 +1,9 @@
1
1
  import type { ReactElement } from "react";
2
2
  import { Room } from "livekit-client";
3
3
  import { MeetingController } from "./meeting-scope";
4
- export declare function MeetingControls({ controller: providedController, spacing, }: {
4
+ export declare function MeetingControls({ controller: providedController, onDisconnect, spacing, }: {
5
5
  controller?: MeetingController;
6
+ onDisconnect?: () => void;
6
7
  spacing?: number;
7
8
  }): ReactElement;
8
9
  export declare function CameraToggle({ controller }: {
@@ -11,8 +12,9 @@ export declare function CameraToggle({ controller }: {
11
12
  export declare function MicToggle({ controller }: {
12
13
  controller?: MeetingController;
13
14
  }): ReactElement | null;
14
- export declare function ConnectionButton({ controller }: {
15
+ export declare function ConnectionButton({ controller, onDisconnect, }: {
15
16
  controller?: MeetingController;
17
+ onDisconnect?: () => void;
16
18
  }): ReactElement;
17
19
  export declare function ChangeSettings({ room }: {
18
20
  room: Room;
@@ -49,6 +49,7 @@ function useControllerVersion(controller) {
49
49
  }
50
50
  function MeetingControls({
51
51
  controller: providedController,
52
+ onDisconnect,
52
53
  spacing = 5
53
54
  }) {
54
55
  const controller = (0, import_meeting_scope.useMeetingController)(providedController);
@@ -60,7 +61,7 @@ function MeetingControls({
60
61
  className: "flex flex-wrap items-center justify-center",
61
62
  style: { gap: spacing },
62
63
  children: [
63
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConnectionButton, { controller }),
64
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConnectionButton, { controller, onDisconnect }),
64
65
  hasLocalParticipant ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
65
66
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MicToggle, { controller }),
66
67
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CameraToggle, { controller }),
@@ -179,7 +180,10 @@ function MicToggle({ controller }) {
179
180
  }
180
181
  );
181
182
  }
182
- function ConnectionButton({ controller }) {
183
+ function ConnectionButton({
184
+ controller,
185
+ onDisconnect
186
+ }) {
183
187
  const resolvedController = (0, import_meeting_scope.useMeetingController)(controller);
184
188
  useControllerVersion(resolvedController);
185
189
  const state = resolvedController.livekitRoom.state;
@@ -191,7 +195,9 @@ function ConnectionButton({ controller }) {
191
195
  destructive: true,
192
196
  icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Phone, {}),
193
197
  onClick: () => {
194
- void resolvedController.disconnect();
198
+ void resolvedController.disconnect().catch((error) => {
199
+ console.warn("unable to disconnect meeting", error);
200
+ }).finally(() => onDisconnect?.());
195
201
  }
196
202
  }
197
203
  );
@@ -0,0 +1,17 @@
1
+ import type { ReactElement } from "react";
2
+ import { type MeetingController, type MeetingFastConnectOptions } from "./meeting-scope";
3
+ export interface MeetingLobbyJoinOptions {
4
+ enableVideo: boolean;
5
+ enableAudio: boolean;
6
+ videoUnavailable: boolean;
7
+ audioUnavailable: boolean;
8
+ videoDeviceId?: string;
9
+ audioDeviceId?: string;
10
+ audioOutputDeviceId?: string;
11
+ }
12
+ export declare function meetingFastConnectOptions(options: MeetingLobbyJoinOptions): MeetingFastConnectOptions;
13
+ export declare function MeetingLobby({ controller: providedController, onCancel, onJoin, }: {
14
+ controller?: MeetingController;
15
+ onCancel?: () => void;
16
+ onJoin?: (options: MeetingLobbyJoinOptions) => void | Promise<void>;
17
+ }): ReactElement;