@meshagent/meshagent-tailwind 0.39.8 → 0.40.0

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 (111) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/cjs/{ChatBotView.js → chat/chat-bot-view.js} +37 -22
  3. package/dist/cjs/{chat-hooks.d.ts → chat/chat-hooks.d.ts} +5 -1
  4. package/dist/cjs/{chat-hooks.js → chat/chat-hooks.js} +12 -2
  5. package/dist/cjs/{ChatInput.js → chat/chat-input.js} +9 -9
  6. package/dist/cjs/chat/chat-thread.d.ts +12 -0
  7. package/dist/cjs/{ChatThread.js → chat/chat-thread.js} +75 -28
  8. package/dist/cjs/{ChatTypingIndicator.js → chat/chat-typing-indicator.js} +4 -4
  9. package/dist/{esm → cjs/chat}/conversation-descriptor.d.ts +7 -1
  10. package/dist/cjs/{conversation-descriptor.js → chat/conversation-descriptor.js} +41 -7
  11. package/dist/cjs/chat/dataset-chat-thread.d.ts +13 -0
  12. package/dist/cjs/chat/dataset-chat-thread.js +1840 -0
  13. package/dist/cjs/{FileUploader.js → chat/file-uploader.js} +4 -4
  14. package/dist/cjs/{multi-thread-view.js → chat/multi-thread-view.js} +8 -3
  15. package/dist/cjs/chat/new-chat-thread.d.ts +17 -0
  16. package/dist/cjs/{Chat.js → chat/new-chat-thread.js} +43 -168
  17. package/dist/cjs/{UploadPill.js → chat/upload-pill.js} +5 -5
  18. package/dist/cjs/file-preview/file-preview.d.ts +34 -0
  19. package/dist/cjs/file-preview/file-preview.js +329 -0
  20. package/dist/cjs/forms/email-address.d.ts +10 -0
  21. package/dist/cjs/forms/email-address.js +105 -0
  22. package/dist/cjs/forms/form.d.ts +27 -0
  23. package/dist/cjs/forms/form.js +200 -0
  24. package/dist/cjs/forms/multi-select-autocomplete.d.ts +35 -0
  25. package/dist/cjs/forms/multi-select-autocomplete.js +294 -0
  26. package/dist/cjs/forms/select-users-dialog.d.ts +20 -0
  27. package/dist/cjs/forms/select-users-dialog.js +145 -0
  28. package/dist/cjs/forms/select-users.d.ts +16 -0
  29. package/dist/cjs/forms/select-users.js +117 -0
  30. package/dist/cjs/index.d.ts +19 -11
  31. package/dist/cjs/index.js +19 -11
  32. package/dist/cjs/meetings/audio-visualization.d.ts +7 -0
  33. package/dist/cjs/meetings/audio-visualization.js +74 -0
  34. package/dist/cjs/meetings/controls.d.ts +19 -0
  35. package/dist/cjs/meetings/controls.js +300 -0
  36. package/dist/cjs/meetings/meeting-scope.d.ts +83 -0
  37. package/dist/cjs/meetings/meeting-scope.js +309 -0
  38. package/dist/cjs/meetings/meetings.d.ts +5 -0
  39. package/dist/cjs/meetings/meetings.js +22 -0
  40. package/dist/cjs/meetings/participants.d.ts +13 -0
  41. package/dist/cjs/meetings/participants.js +154 -0
  42. package/dist/cjs/meetings/wake-lock.d.ts +4 -0
  43. package/dist/cjs/meetings/wake-lock.js +55 -0
  44. package/dist/esm/{ChatBotView.js → chat/chat-bot-view.js} +34 -19
  45. package/dist/esm/{chat-hooks.d.ts → chat/chat-hooks.d.ts} +5 -1
  46. package/dist/esm/{chat-hooks.js → chat/chat-hooks.js} +12 -2
  47. package/dist/esm/{ChatInput.js → chat/chat-input.js} +4 -4
  48. package/dist/esm/chat/chat-thread.d.ts +12 -0
  49. package/dist/esm/{ChatThread.js → chat/chat-thread.js} +70 -23
  50. package/dist/esm/{ChatTypingIndicator.js → chat/chat-typing-indicator.js} +1 -1
  51. package/dist/{cjs → esm/chat}/conversation-descriptor.d.ts +7 -1
  52. package/dist/esm/{conversation-descriptor.js → chat/conversation-descriptor.js} +41 -7
  53. package/dist/esm/chat/dataset-chat-thread.d.ts +13 -0
  54. package/dist/esm/chat/dataset-chat-thread.js +1815 -0
  55. package/dist/esm/{FileUploader.js → chat/file-uploader.js} +1 -1
  56. package/dist/esm/{multi-thread-view.js → chat/multi-thread-view.js} +8 -3
  57. package/dist/esm/chat/new-chat-thread.d.ts +17 -0
  58. package/dist/esm/{Chat.js → chat/new-chat-thread.js} +40 -165
  59. package/dist/esm/{UploadPill.js → chat/upload-pill.js} +2 -2
  60. package/dist/esm/file-preview/file-preview.d.ts +34 -0
  61. package/dist/esm/file-preview/file-preview.js +316 -0
  62. package/dist/esm/forms/email-address.d.ts +10 -0
  63. package/dist/esm/forms/email-address.js +85 -0
  64. package/dist/esm/forms/form.d.ts +27 -0
  65. package/dist/esm/forms/form.js +193 -0
  66. package/dist/esm/forms/multi-select-autocomplete.d.ts +35 -0
  67. package/dist/esm/forms/multi-select-autocomplete.js +274 -0
  68. package/dist/esm/forms/select-users-dialog.d.ts +20 -0
  69. package/dist/esm/forms/select-users-dialog.js +132 -0
  70. package/dist/esm/forms/select-users.d.ts +16 -0
  71. package/dist/esm/forms/select-users.js +97 -0
  72. package/dist/esm/index.d.ts +19 -11
  73. package/dist/esm/index.js +19 -11
  74. package/dist/esm/meetings/audio-visualization.d.ts +7 -0
  75. package/dist/esm/meetings/audio-visualization.js +54 -0
  76. package/dist/esm/meetings/controls.d.ts +19 -0
  77. package/dist/esm/meetings/controls.js +294 -0
  78. package/dist/esm/meetings/meeting-scope.d.ts +83 -0
  79. package/dist/esm/meetings/meeting-scope.js +294 -0
  80. package/dist/esm/meetings/meetings.d.ts +5 -0
  81. package/dist/esm/meetings/meetings.js +5 -0
  82. package/dist/esm/meetings/participants.d.ts +13 -0
  83. package/dist/esm/meetings/participants.js +137 -0
  84. package/dist/esm/meetings/wake-lock.d.ts +4 -0
  85. package/dist/esm/meetings/wake-lock.js +35 -0
  86. package/dist/index.css +2 -2
  87. package/package.json +7 -4
  88. package/dist/cjs/Chat.d.ts +0 -15
  89. package/dist/cjs/ChatThread.d.ts +0 -21
  90. package/dist/esm/Chat.d.ts +0 -15
  91. package/dist/esm/ChatThread.d.ts +0 -21
  92. /package/dist/cjs/{ChatBotView.d.ts → chat/chat-bot-view.d.ts} +0 -0
  93. /package/dist/cjs/{ChatInput.d.ts → chat/chat-input.d.ts} +0 -0
  94. /package/dist/cjs/{chat-message.d.ts → chat/chat-message.d.ts} +0 -0
  95. /package/dist/cjs/{chat-message.js → chat/chat-message.js} +0 -0
  96. /package/dist/cjs/{ChatTypingIndicator.d.ts → chat/chat-typing-indicator.d.ts} +0 -0
  97. /package/dist/cjs/{file-attachment.d.ts → chat/file-attachment.d.ts} +0 -0
  98. /package/dist/cjs/{file-attachment.js → chat/file-attachment.js} +0 -0
  99. /package/dist/cjs/{FileUploader.d.ts → chat/file-uploader.d.ts} +0 -0
  100. /package/dist/cjs/{multi-thread-view.d.ts → chat/multi-thread-view.d.ts} +0 -0
  101. /package/dist/cjs/{UploadPill.d.ts → chat/upload-pill.d.ts} +0 -0
  102. /package/dist/esm/{ChatBotView.d.ts → chat/chat-bot-view.d.ts} +0 -0
  103. /package/dist/esm/{ChatInput.d.ts → chat/chat-input.d.ts} +0 -0
  104. /package/dist/esm/{chat-message.d.ts → chat/chat-message.d.ts} +0 -0
  105. /package/dist/esm/{chat-message.js → chat/chat-message.js} +0 -0
  106. /package/dist/esm/{ChatTypingIndicator.d.ts → chat/chat-typing-indicator.d.ts} +0 -0
  107. /package/dist/esm/{file-attachment.d.ts → chat/file-attachment.d.ts} +0 -0
  108. /package/dist/esm/{file-attachment.js → chat/file-attachment.js} +0 -0
  109. /package/dist/esm/{FileUploader.d.ts → chat/file-uploader.d.ts} +0 -0
  110. /package/dist/esm/{multi-thread-view.d.ts → chat/multi-thread-view.d.ts} +0 -0
  111. /package/dist/esm/{UploadPill.d.ts → chat/upload-pill.d.ts} +0 -0
package/dist/cjs/index.js CHANGED
@@ -15,14 +15,22 @@ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "defau
15
15
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
16
16
  var index_exports = {};
17
17
  module.exports = __toCommonJS(index_exports);
18
- __reExport(index_exports, require("./Chat"), module.exports);
19
- __reExport(index_exports, require("./ChatBotView"), module.exports);
20
- __reExport(index_exports, require("./ChatInput"), module.exports);
21
- __reExport(index_exports, require("./ChatThread"), module.exports);
22
- __reExport(index_exports, require("./ChatTypingIndicator"), module.exports);
23
- __reExport(index_exports, require("./FileUploader"), module.exports);
24
- __reExport(index_exports, require("./chat-message"), module.exports);
25
- __reExport(index_exports, require("./conversation-descriptor"), module.exports);
26
- __reExport(index_exports, require("./file-attachment"), module.exports);
27
- __reExport(index_exports, require("./multi-thread-view"), module.exports);
28
- __reExport(index_exports, require("./chat-hooks"), module.exports);
18
+ __reExport(index_exports, require("./chat/chat-bot-view"), module.exports);
19
+ __reExport(index_exports, require("./chat/chat-hooks"), module.exports);
20
+ __reExport(index_exports, require("./chat/chat-input"), module.exports);
21
+ __reExport(index_exports, require("./chat/chat-message"), module.exports);
22
+ __reExport(index_exports, require("./chat/chat-thread"), module.exports);
23
+ __reExport(index_exports, require("./chat/chat-typing-indicator"), module.exports);
24
+ __reExport(index_exports, require("./chat/conversation-descriptor"), module.exports);
25
+ __reExport(index_exports, require("./chat/dataset-chat-thread"), module.exports);
26
+ __reExport(index_exports, require("./chat/file-attachment"), module.exports);
27
+ __reExport(index_exports, require("./chat/file-uploader"), module.exports);
28
+ __reExport(index_exports, require("./chat/multi-thread-view"), module.exports);
29
+ __reExport(index_exports, require("./chat/new-chat-thread"), module.exports);
30
+ __reExport(index_exports, require("./file-preview/file-preview"), module.exports);
31
+ __reExport(index_exports, require("./forms/email-address"), module.exports);
32
+ __reExport(index_exports, require("./forms/form"), module.exports);
33
+ __reExport(index_exports, require("./forms/multi-select-autocomplete"), module.exports);
34
+ __reExport(index_exports, require("./forms/select-users"), module.exports);
35
+ __reExport(index_exports, require("./forms/select-users-dialog"), module.exports);
36
+ __reExport(index_exports, require("./meetings/meetings"), module.exports);
@@ -0,0 +1,7 @@
1
+ import type { ReactElement } from "react";
2
+ import type { Participant, Room } from "livekit-client";
3
+ export declare function AudioWave({ room: _room, participant, className, }: {
4
+ room: Room;
5
+ participant: Participant;
6
+ className?: string;
7
+ }): ReactElement;
@@ -0,0 +1,74 @@
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 audio_visualization_exports = {};
20
+ __export(audio_visualization_exports, {
21
+ AudioWave: () => AudioWave
22
+ });
23
+ module.exports = __toCommonJS(audio_visualization_exports);
24
+ var import_jsx_runtime = require("react/jsx-runtime");
25
+ var import_react = require("react");
26
+ function useParticipantSnapshot(participant) {
27
+ (0, import_react.useSyncExternalStore)(
28
+ (listener) => {
29
+ const events = ["isSpeakingChanged", "attributesChanged", "trackMuted", "trackUnmuted"];
30
+ for (const eventName of events) {
31
+ participant.on(eventName, listener);
32
+ }
33
+ return () => {
34
+ for (const eventName of events) {
35
+ participant.off(eventName, listener);
36
+ }
37
+ };
38
+ },
39
+ () => `${participant.isSpeaking}:${participant.audioLevel}:${participant.attributes["lk.agent.state"] ?? ""}`,
40
+ () => ""
41
+ );
42
+ }
43
+ function AudioWave({
44
+ room: _room,
45
+ participant,
46
+ className
47
+ }) {
48
+ useParticipantSnapshot(participant);
49
+ const [, setFrame] = (0, import_react.useState)(0);
50
+ const bars = (0, import_react.useRef)(Array.from({ length: 28 }, (_, index) => 0.25 + index % 7 * 0.08));
51
+ const thinking = participant.attributes["lk.agent.state"] === "thinking";
52
+ const amplitude = thinking ? 0.2 : Math.max(participant.audioLevel, participant.isSpeaking ? 0.45 : 0.12);
53
+ (0, import_react.useEffect)(() => {
54
+ const interval = window.setInterval(() => {
55
+ bars.current = bars.current.map((value, index) => {
56
+ const phase = Date.now() / 180 + index * 0.65;
57
+ return Math.max(0.12, Math.min(1, value * 0.6 + Math.abs(Math.sin(phase)) * amplitude * 0.8));
58
+ });
59
+ setFrame((frame) => frame + 1);
60
+ }, 1e3 / 30);
61
+ return () => window.clearInterval(interval);
62
+ }, [amplitude]);
63
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: className ?? "flex h-full w-full items-center justify-center bg-background", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex h-24 w-4/5 max-w-xl items-center justify-center gap-1 opacity-80", children: bars.current.map((height, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
64
+ "div",
65
+ {
66
+ className: "w-1 rounded-full bg-foreground/30 transition-[height,background-color]",
67
+ style: {
68
+ height: `${Math.max(10, height * 96)}px`,
69
+ backgroundColor: participant.isSpeaking ? "hsl(var(--foreground) / 0.42)" : "hsl(var(--foreground) / 0.18)"
70
+ }
71
+ },
72
+ index
73
+ )) }) });
74
+ }
@@ -0,0 +1,19 @@
1
+ import type { ReactElement } from "react";
2
+ import { Room } from "livekit-client";
3
+ import { MeetingController } from "./meeting-scope";
4
+ export declare function MeetingControls({ controller: providedController, spacing, }: {
5
+ controller?: MeetingController;
6
+ spacing?: number;
7
+ }): ReactElement;
8
+ export declare function CameraToggle({ controller }: {
9
+ controller?: MeetingController;
10
+ }): ReactElement | null;
11
+ export declare function MicToggle({ controller }: {
12
+ controller?: MeetingController;
13
+ }): ReactElement | null;
14
+ export declare function ConnectionButton({ controller }: {
15
+ controller?: MeetingController;
16
+ }): ReactElement;
17
+ export declare function ChangeSettings({ room }: {
18
+ room: Room;
19
+ }): ReactElement;
@@ -0,0 +1,300 @@
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 controls_exports = {};
20
+ __export(controls_exports, {
21
+ CameraToggle: () => CameraToggle,
22
+ ChangeSettings: () => ChangeSettings,
23
+ ConnectionButton: () => ConnectionButton,
24
+ MeetingControls: () => MeetingControls,
25
+ MicToggle: () => MicToggle
26
+ });
27
+ module.exports = __toCommonJS(controls_exports);
28
+ var import_jsx_runtime = require("react/jsx-runtime");
29
+ var import_react = require("react");
30
+ var import_lucide_react = require("lucide-react");
31
+ var import_livekit_client = require("livekit-client");
32
+ var import_button = require("../components/ui/button");
33
+ var import_dialog = require("../components/ui/dialog");
34
+ var import_select = require("../components/ui/select");
35
+ var import_spinner = require("../components/ui/spinner");
36
+ var import_utils = require("../lib/utils");
37
+ var import_meeting_scope = require("./meeting-scope");
38
+ function useControllerVersion(controller) {
39
+ (0, import_react.useSyncExternalStore)(
40
+ (listener) => controller.subscribe(listener),
41
+ () => controller.livekitRoom.state,
42
+ () => controller.livekitRoom.state
43
+ );
44
+ (0, import_react.useSyncExternalStore)(
45
+ (listener) => controller.pendingLocalMedia.subscribe(listener),
46
+ () => `${controller.pendingLocalMedia.cameraPending}:${controller.pendingLocalMedia.microphonePending}:${controller.pendingLocalMedia.cameraUnavailable}:${controller.pendingLocalMedia.microphoneUnavailable}`,
47
+ () => ""
48
+ );
49
+ }
50
+ function MeetingControls({
51
+ controller: providedController,
52
+ spacing = 5
53
+ }) {
54
+ const controller = (0, import_meeting_scope.useMeetingController)(providedController);
55
+ useControllerVersion(controller);
56
+ const hasLocalParticipant = controller.livekitRoom.localParticipant != null;
57
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
58
+ "div",
59
+ {
60
+ className: "flex flex-wrap items-center justify-center",
61
+ style: { gap: spacing },
62
+ children: [
63
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConnectionButton, { controller }),
64
+ hasLocalParticipant ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
65
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MicToggle, { controller }),
66
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CameraToggle, { controller }),
67
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChangeSettings, { room: controller.livekitRoom })
68
+ ] }) : null
69
+ ]
70
+ }
71
+ );
72
+ }
73
+ function describeCameraToggleError(error) {
74
+ const message = String(error);
75
+ if (message.includes("NotAllowedError")) {
76
+ return "Camera access was blocked by the browser or system.";
77
+ }
78
+ if (message.includes("NotFoundError")) {
79
+ return "The selected camera was not found.";
80
+ }
81
+ return `Unable to change camera state: ${message}`;
82
+ }
83
+ function describeMicrophoneToggleError(error) {
84
+ const message = String(error);
85
+ if (message.includes("NotAllowedError")) {
86
+ return "Microphone access was blocked by the browser or system.";
87
+ }
88
+ if (message.includes("NotFoundError")) {
89
+ return "The selected microphone was not found.";
90
+ }
91
+ return `Unable to change microphone state: ${message}`;
92
+ }
93
+ function useDeviceAvailable(kind) {
94
+ const [available, setAvailable] = (0, import_react.useState)(true);
95
+ (0, import_react.useEffect)(() => {
96
+ let cancelled = false;
97
+ const update = async () => {
98
+ const devices = await navigator.mediaDevices?.enumerateDevices?.();
99
+ if (!cancelled && devices != null) {
100
+ setAvailable(devices.some((device) => device.kind === kind && device.deviceId !== ""));
101
+ }
102
+ };
103
+ void update().catch(() => setAvailable(false));
104
+ navigator.mediaDevices?.addEventListener?.("devicechange", update);
105
+ return () => {
106
+ cancelled = true;
107
+ navigator.mediaDevices?.removeEventListener?.("devicechange", update);
108
+ };
109
+ }, [kind]);
110
+ return available;
111
+ }
112
+ function CameraToggle({ controller }) {
113
+ const resolvedController = (0, import_meeting_scope.useMeetingController)(controller);
114
+ useControllerVersion(resolvedController);
115
+ const [processing, setProcessing] = (0, import_react.useState)(false);
116
+ const deviceAvailable = useDeviceAvailable("videoinput");
117
+ const localParticipant = resolvedController.livekitRoom.localParticipant;
118
+ if (localParticipant == null) {
119
+ return null;
120
+ }
121
+ const enabled = localParticipant.isCameraEnabled;
122
+ const pending = resolvedController.pendingLocalMedia.cameraPending;
123
+ const showEnabled = enabled || pending;
124
+ const unavailable = (resolvedController.pendingLocalMedia.cameraUnavailable || !deviceAvailable) && !showEnabled;
125
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
126
+ MeetingControlsButton,
127
+ {
128
+ text: pending ? "Starting camera" : enabled ? "Turn off camera" : "Turn on camera",
129
+ on: showEnabled,
130
+ destructive: unavailable,
131
+ icon: showEnabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Camera, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.CameraOff, {}),
132
+ loading: pending || processing,
133
+ disabled: processing || pending,
134
+ onClick: () => {
135
+ if (processing || pending) {
136
+ return;
137
+ }
138
+ setProcessing(true);
139
+ void localParticipant.setCameraEnabled(!enabled).then(() => resolvedController.pendingLocalMedia.setCameraUnavailable(false)).catch((error) => {
140
+ resolvedController.pendingLocalMedia.setCameraUnavailable(true);
141
+ console.warn(describeCameraToggleError(error));
142
+ }).finally(() => setProcessing(false));
143
+ }
144
+ }
145
+ );
146
+ }
147
+ function MicToggle({ controller }) {
148
+ const resolvedController = (0, import_meeting_scope.useMeetingController)(controller);
149
+ useControllerVersion(resolvedController);
150
+ const [processing, setProcessing] = (0, import_react.useState)(false);
151
+ const deviceAvailable = useDeviceAvailable("audioinput");
152
+ const localParticipant = resolvedController.livekitRoom.localParticipant;
153
+ if (localParticipant == null) {
154
+ return null;
155
+ }
156
+ const enabled = localParticipant.isMicrophoneEnabled;
157
+ const pending = resolvedController.pendingLocalMedia.microphonePending;
158
+ const showEnabled = enabled || pending;
159
+ const unavailable = (resolvedController.pendingLocalMedia.microphoneUnavailable || !deviceAvailable) && !showEnabled;
160
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
161
+ MeetingControlsButton,
162
+ {
163
+ text: pending ? "Starting mic" : enabled ? "Turn off mic" : "Turn on mic",
164
+ on: showEnabled,
165
+ destructive: unavailable,
166
+ icon: showEnabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Mic, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.MicOff, {}),
167
+ loading: pending || processing,
168
+ disabled: processing || pending,
169
+ onClick: () => {
170
+ if (processing || pending) {
171
+ return;
172
+ }
173
+ setProcessing(true);
174
+ void localParticipant.setMicrophoneEnabled(!enabled).then(() => resolvedController.pendingLocalMedia.setMicrophoneUnavailable(false)).catch((error) => {
175
+ resolvedController.pendingLocalMedia.setMicrophoneUnavailable(true);
176
+ console.warn(describeMicrophoneToggleError(error));
177
+ }).finally(() => setProcessing(false));
178
+ }
179
+ }
180
+ );
181
+ }
182
+ function ConnectionButton({ controller }) {
183
+ const resolvedController = (0, import_meeting_scope.useMeetingController)(controller);
184
+ useControllerVersion(resolvedController);
185
+ const state = resolvedController.livekitRoom.state;
186
+ if (state === import_livekit_client.ConnectionState.Connected) {
187
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
188
+ MeetingControlsButton,
189
+ {
190
+ text: "Hangup",
191
+ destructive: true,
192
+ icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Phone, {}),
193
+ onClick: () => {
194
+ void resolvedController.disconnect();
195
+ }
196
+ }
197
+ );
198
+ }
199
+ if (state === import_livekit_client.ConnectionState.Disconnected) {
200
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
201
+ MeetingControlsButton,
202
+ {
203
+ text: "Connect",
204
+ icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Phone, {}),
205
+ onClick: () => {
206
+ void resolvedController.connect();
207
+ }
208
+ }
209
+ );
210
+ }
211
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MeetingControlsButton, { text: "Connecting", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Phone, {}), loading: true, disabled: true });
212
+ }
213
+ function MeetingControlsButton({
214
+ text,
215
+ icon,
216
+ on,
217
+ destructive,
218
+ loading,
219
+ disabled,
220
+ onClick
221
+ }) {
222
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
223
+ import_button.Button,
224
+ {
225
+ type: "button",
226
+ title: text,
227
+ "aria-label": text,
228
+ size: "icon",
229
+ variant: destructive ? "destructive" : on ? "default" : "outline",
230
+ disabled,
231
+ onClick,
232
+ className: (0, import_utils.cn)("h-12 w-12", on && !destructive ? "bg-emerald-600 text-white hover:bg-emerald-700" : null),
233
+ children: loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_spinner.Spinner, { className: "h-5 w-5" }) : icon
234
+ }
235
+ );
236
+ }
237
+ function deviceLabel(device, fallbackPrefix) {
238
+ const label = device?.label.trim();
239
+ return label != null && label !== "" ? label.replace(/^Default - /u, "") : `Default ${fallbackPrefix}`;
240
+ }
241
+ function devicesForKind(devices, kind) {
242
+ return devices.filter((device) => device.kind === kind && device.deviceId !== "");
243
+ }
244
+ function DeviceSelect({
245
+ label,
246
+ devices,
247
+ kind,
248
+ room
249
+ }) {
250
+ const options = devicesForKind(devices, kind);
251
+ const activeDevice = room.getActiveDevice(kind) ?? options[0]?.deviceId ?? "";
252
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "grid gap-2", children: [
253
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-sm font-medium", children: label }),
254
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
255
+ import_select.Select,
256
+ {
257
+ value: activeDevice,
258
+ onValueChange: (deviceId) => {
259
+ void room.switchActiveDevice(kind, deviceId).catch((error) => {
260
+ console.warn(`Unable to switch ${label.toLowerCase()}`, error);
261
+ });
262
+ },
263
+ children: [
264
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_select.SelectTrigger, { className: "w-full", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_select.SelectValue, { placeholder: deviceLabel(options[0] ?? null, label) }) }),
265
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_select.SelectContent, { children: options.map((device) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_select.SelectItem, { value: device.deviceId, children: deviceLabel(device, label) }, device.deviceId)) })
266
+ ]
267
+ }
268
+ )
269
+ ] });
270
+ }
271
+ function ChangeSettings({ room }) {
272
+ const [devices, setDevices] = (0, import_react.useState)([]);
273
+ const refreshDevices = (0, import_react.useCallback)(() => {
274
+ void import_livekit_client.Room.getLocalDevices(void 0, false).then(setDevices).catch(() => setDevices([]));
275
+ }, []);
276
+ (0, import_react.useEffect)(() => {
277
+ refreshDevices();
278
+ navigator.mediaDevices?.addEventListener?.("devicechange", refreshDevices);
279
+ return () => navigator.mediaDevices?.removeEventListener?.("devicechange", refreshDevices);
280
+ }, [refreshDevices]);
281
+ const hasAudioOutput = (0, import_react.useMemo)(
282
+ () => devices.some((device) => device.kind === "audiooutput"),
283
+ [devices]
284
+ );
285
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_dialog.Dialog, { children: [
286
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dialog.DialogTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_button.Button, { type: "button", title: "Change settings", "aria-label": "Change settings", variant: "outline", size: "icon", className: "h-12 w-12", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Settings, {}) }) }),
287
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_dialog.DialogContent, { className: "max-w-[min(92vw,560px)]", children: [
288
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_dialog.DialogHeader, { children: [
289
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dialog.DialogTitle, { children: "Meeting settings" }),
290
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dialog.DialogDescription, { children: "Choose the devices used for this meeting." })
291
+ ] }),
292
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "grid gap-4", children: [
293
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DeviceSelect, { label: "Camera", devices, kind: "videoinput", room }),
294
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DeviceSelect, { label: "Microphone", devices, kind: "audioinput", room }),
295
+ hasAudioOutput ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DeviceSelect, { label: "Speaker", devices, kind: "audiooutput", room }) : null
296
+ ] }),
297
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dialog.DialogFooter, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_button.Button, { type: "button", variant: "outline", onClick: refreshDevices, children: "Refresh" }) })
298
+ ] })
299
+ ] });
300
+ }
@@ -0,0 +1,83 @@
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";
4
+ type Listener = () => void;
5
+ export interface LivekitConnectionInfo {
6
+ url: string;
7
+ token: string;
8
+ }
9
+ export type MeetingFastConnectOptions = RoomConnectOptions & {
10
+ camera?: {
11
+ enabled?: boolean;
12
+ };
13
+ microphone?: {
14
+ enabled?: boolean;
15
+ };
16
+ };
17
+ export declare class PendingLocalMediaState {
18
+ private listeners;
19
+ private cameraAwaitingEnableConfirmation;
20
+ private microphoneAwaitingEnableConfirmation;
21
+ private _cameraPending;
22
+ private _microphonePending;
23
+ private _cameraUnavailable;
24
+ private _microphoneUnavailable;
25
+ get cameraPending(): boolean;
26
+ get microphonePending(): boolean;
27
+ get cameraUnavailable(): boolean;
28
+ get microphoneUnavailable(): boolean;
29
+ subscribe(listener: Listener): () => void;
30
+ setCameraPending(value: boolean, { awaitEnableConfirmation }?: {
31
+ awaitEnableConfirmation?: boolean | undefined;
32
+ }): void;
33
+ setMicrophonePending(value: boolean, { awaitEnableConfirmation }?: {
34
+ awaitEnableConfirmation?: boolean | undefined;
35
+ }): void;
36
+ setCameraUnavailable(value: boolean): void;
37
+ setMicrophoneUnavailable(value: boolean): void;
38
+ setPending({ cameraPending, microphonePending, cameraAwaitEnableConfirmation, microphoneAwaitEnableConfirmation, }: {
39
+ cameraPending: boolean;
40
+ microphonePending: boolean;
41
+ cameraAwaitEnableConfirmation?: boolean;
42
+ microphoneAwaitEnableConfirmation?: boolean;
43
+ }): void;
44
+ syncFromLocalParticipant(participant: LocalParticipant | undefined, disconnected: boolean): void;
45
+ clear(): void;
46
+ dispose(): void;
47
+ private notify;
48
+ }
49
+ export declare class MeetingController {
50
+ readonly room: RoomClient;
51
+ readonly livekitRoom: Room;
52
+ readonly pendingLocalMedia: PendingLocalMediaState;
53
+ private readonly listeners;
54
+ private _config;
55
+ private _configurationError;
56
+ constructor({ room, roomOptions }: {
57
+ room: RoomClient;
58
+ roomOptions?: RoomOptions;
59
+ });
60
+ get config(): LivekitConnectionInfo | null;
61
+ get configurationError(): unknown;
62
+ get isConnected(): boolean;
63
+ get hasParticipantsWithVideo(): boolean;
64
+ subscribe(listener: Listener): () => void;
65
+ configure({ breakoutRoom }?: {
66
+ breakoutRoom?: string;
67
+ }): Promise<void>;
68
+ connect(options?: MeetingFastConnectOptions): Promise<void>;
69
+ disconnect(): Promise<void>;
70
+ dispose(): void;
71
+ private onRoomChanged;
72
+ private syncPendingLocalMediaState;
73
+ private notify;
74
+ }
75
+ export declare function useMeetingController(controller?: MeetingController): MeetingController;
76
+ export declare function MeetingScope({ client, breakoutRoom, roomOptions, children, }: {
77
+ client: RoomClient;
78
+ breakoutRoom?: string;
79
+ roomOptions?: RoomOptions;
80
+ children: ReactNode | ((controller: MeetingController) => ReactNode);
81
+ }): ReactElement;
82
+ export declare function firstEnabledVideoPublication(participant: Participant): TrackPublication | undefined;
83
+ export {};