@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
@@ -0,0 +1,274 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
3
+ import { X } from "lucide-react";
4
+ import { cn } from "../lib/utils";
5
+ class MultiSelectController {
6
+ listeners = /* @__PURE__ */ new Set();
7
+ _value;
8
+ constructor(initialValue = []) {
9
+ this._value = [...initialValue];
10
+ }
11
+ get value() {
12
+ return [...this._value];
13
+ }
14
+ set value(nextValue) {
15
+ this._value = [...nextValue];
16
+ this.notify();
17
+ }
18
+ async add(item) {
19
+ if (!await this.canAddItem(item)) {
20
+ return false;
21
+ }
22
+ this.value = [...this._value, item];
23
+ return true;
24
+ }
25
+ canAddItem(_item) {
26
+ return true;
27
+ }
28
+ remove(item) {
29
+ this.value = this._value.filter((current) => current !== item);
30
+ }
31
+ removeLast() {
32
+ if (this._value.length > 0) {
33
+ this.value = this._value.slice(0, -1);
34
+ }
35
+ }
36
+ clear() {
37
+ this.value = [];
38
+ }
39
+ subscribe(listener) {
40
+ this.listeners.add(listener);
41
+ return () => {
42
+ this.listeners.delete(listener);
43
+ };
44
+ }
45
+ dispose() {
46
+ this.listeners.clear();
47
+ }
48
+ notify() {
49
+ const value = this.value;
50
+ for (const listener of this.listeners) {
51
+ listener(value);
52
+ }
53
+ }
54
+ }
55
+ function containsSelected(selected, value) {
56
+ const normalized = value.toLowerCase();
57
+ return selected.some((item) => item.toLowerCase() === normalized);
58
+ }
59
+ function MultiSelectAutocomplete({
60
+ search,
61
+ onChanged,
62
+ controller,
63
+ placeholder = "Type a value",
64
+ debounceDuration = 300,
65
+ minimumSearchLength = 2,
66
+ initialValue = [],
67
+ value,
68
+ autoFocus = false,
69
+ className,
70
+ inputClassName,
71
+ onTextChanged
72
+ }) {
73
+ const generatedController = useMemo(() => controller ?? new MultiSelectController(initialValue), [controller]);
74
+ const [selected, setSelected] = useState(() => value ?? generatedController.value);
75
+ const [text, setText] = useState("");
76
+ const [options, setOptions] = useState([]);
77
+ const [selectedOption, setSelectedOption] = useState(0);
78
+ const [open, setOpen] = useState(false);
79
+ const sequence = useRef(0);
80
+ const inputRef = useRef(null);
81
+ const listboxId = useId();
82
+ useEffect(() => {
83
+ if (value != null) {
84
+ setSelected(value);
85
+ generatedController.value = value;
86
+ }
87
+ }, [generatedController, value]);
88
+ useEffect(() => {
89
+ return generatedController.subscribe((nextValue) => {
90
+ setSelected(nextValue);
91
+ onChanged?.(nextValue);
92
+ });
93
+ }, [generatedController, onChanged]);
94
+ useEffect(() => {
95
+ if (controller == null) {
96
+ return () => {
97
+ generatedController.dispose();
98
+ };
99
+ }
100
+ return void 0;
101
+ }, [controller, generatedController]);
102
+ useEffect(() => {
103
+ const query = text.trim();
104
+ if (query.length < minimumSearchLength) {
105
+ setOpen(false);
106
+ setOptions([]);
107
+ return void 0;
108
+ }
109
+ const currentSequence = sequence.current + 1;
110
+ sequence.current = currentSequence;
111
+ const timeout = window.setTimeout(() => {
112
+ void Promise.resolve(search(query)).then((results) => {
113
+ if (sequence.current !== currentSequence) {
114
+ return;
115
+ }
116
+ const filtered = results.filter((item) => !containsSelected(generatedController.value, item));
117
+ setOptions(filtered);
118
+ setSelectedOption(0);
119
+ setOpen(filtered.length > 0);
120
+ });
121
+ }, debounceDuration);
122
+ return () => {
123
+ window.clearTimeout(timeout);
124
+ };
125
+ }, [debounceDuration, generatedController, minimumSearchLength, search, text]);
126
+ const add = useCallback(async (item) => {
127
+ const trimmed = item.trim();
128
+ if (trimmed === "") {
129
+ return;
130
+ }
131
+ const added = await generatedController.add(trimmed);
132
+ if (!added) {
133
+ return;
134
+ }
135
+ setText("");
136
+ setOpen(false);
137
+ setOptions([]);
138
+ inputRef.current?.focus();
139
+ }, [generatedController]);
140
+ const remove = useCallback((item) => {
141
+ generatedController.remove(item);
142
+ inputRef.current?.focus();
143
+ }, [generatedController]);
144
+ const handleTextChange = useCallback((nextText) => {
145
+ setText(nextText);
146
+ onTextChanged?.(nextText, {
147
+ add,
148
+ setText,
149
+ controller: generatedController
150
+ });
151
+ }, [add, generatedController, onTextChanged]);
152
+ const handleKeyDown = useCallback((event) => {
153
+ if (event.key === "Backspace" && text === "") {
154
+ generatedController.removeLast();
155
+ return;
156
+ }
157
+ if (event.key === "Escape") {
158
+ setOpen(false);
159
+ return;
160
+ }
161
+ if (event.key === "ArrowDown") {
162
+ event.preventDefault();
163
+ if (options.length > 0) {
164
+ setOpen(true);
165
+ setSelectedOption((index) => Math.min(index + 1, options.length - 1));
166
+ }
167
+ return;
168
+ }
169
+ if (event.key === "ArrowUp") {
170
+ event.preventDefault();
171
+ if (options.length > 0) {
172
+ setSelectedOption((index) => Math.max(index - 1, 0));
173
+ }
174
+ return;
175
+ }
176
+ if (event.key === "Enter") {
177
+ event.preventDefault();
178
+ const option = open ? options[selectedOption] : void 0;
179
+ void add(option ?? text);
180
+ }
181
+ }, [add, generatedController, open, options, selectedOption, text]);
182
+ return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
183
+ /* @__PURE__ */ jsxs(
184
+ "div",
185
+ {
186
+ className: cn(
187
+ "border-input focus-within:border-ring focus-within:ring-ring/50 flex min-h-9 w-full flex-wrap items-center gap-2 rounded-md border bg-transparent px-2 py-1.5 text-sm shadow-xs transition-[color,box-shadow] focus-within:ring-[3px]",
188
+ className
189
+ ),
190
+ onClick: () => inputRef.current?.focus(),
191
+ children: [
192
+ selected.map((item) => /* @__PURE__ */ jsxs(
193
+ "span",
194
+ {
195
+ className: "inline-flex max-w-full items-center gap-1 rounded-md bg-primary px-2 py-1 text-xs font-medium text-primary-foreground",
196
+ children: [
197
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: item }),
198
+ /* @__PURE__ */ jsx(
199
+ "button",
200
+ {
201
+ type: "button",
202
+ className: "rounded-sm opacity-80 transition-opacity hover:opacity-100 focus:outline-none focus:ring-1 focus:ring-primary-foreground",
203
+ onClick: (event) => {
204
+ event.stopPropagation();
205
+ remove(item);
206
+ },
207
+ "aria-label": `Remove ${item}`,
208
+ children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
209
+ }
210
+ )
211
+ ]
212
+ },
213
+ item
214
+ )),
215
+ /* @__PURE__ */ jsx(
216
+ "input",
217
+ {
218
+ ref: inputRef,
219
+ value: text,
220
+ autoFocus,
221
+ onChange: (event) => handleTextChange(event.currentTarget.value),
222
+ onKeyDown: handleKeyDown,
223
+ onBlur: () => {
224
+ window.setTimeout(() => setOpen(false), 120);
225
+ },
226
+ placeholder: selected.length === 0 ? placeholder : void 0,
227
+ className: cn(
228
+ "min-w-24 flex-1 bg-transparent px-1 py-0.5 outline-none placeholder:text-muted-foreground",
229
+ inputClassName
230
+ ),
231
+ role: "combobox",
232
+ "aria-expanded": open,
233
+ "aria-controls": open ? listboxId : void 0,
234
+ "aria-autocomplete": "list"
235
+ }
236
+ )
237
+ ]
238
+ }
239
+ ),
240
+ open ? /* @__PURE__ */ jsx(
241
+ "div",
242
+ {
243
+ id: listboxId,
244
+ role: "listbox",
245
+ className: "absolute left-0 top-full z-50 mt-2 max-h-80 w-full min-w-64 overflow-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
246
+ children: options.map((option, index) => /* @__PURE__ */ jsx(
247
+ "button",
248
+ {
249
+ type: "button",
250
+ role: "option",
251
+ "aria-selected": index === selectedOption,
252
+ className: cn(
253
+ "flex h-9 w-full items-center rounded-sm px-2 text-left text-sm outline-none transition-colors",
254
+ index === selectedOption ? "bg-accent text-accent-foreground" : "hover:bg-accent hover:text-accent-foreground"
255
+ ),
256
+ onMouseEnter: () => setSelectedOption(index),
257
+ onMouseDown: (event) => {
258
+ event.preventDefault();
259
+ },
260
+ onClick: () => {
261
+ void add(option);
262
+ },
263
+ children: /* @__PURE__ */ jsx("span", { className: "truncate", children: option })
264
+ },
265
+ option
266
+ ))
267
+ }
268
+ ) : null
269
+ ] });
270
+ }
271
+ export {
272
+ MultiSelectAutocomplete,
273
+ MultiSelectController
274
+ };
@@ -0,0 +1,20 @@
1
+ import type { ReactElement } from "react";
2
+ export declare function showSelectUsersDialog({ projectEmails, initialValue, title, description, confirmLabel, cancelLabel, }: {
3
+ projectEmails: string[];
4
+ initialValue?: string[];
5
+ title?: string;
6
+ description?: string;
7
+ confirmLabel?: string;
8
+ cancelLabel?: string;
9
+ }): Promise<string[] | null>;
10
+ export declare function SelectUsersDialog({ projectEmails, initialValue, title, description, confirmLabel, cancelLabel, onSubmit, onCancel, onCleanup, }: {
11
+ projectEmails: string[];
12
+ initialValue?: string[];
13
+ title?: string;
14
+ description?: string;
15
+ confirmLabel?: string;
16
+ cancelLabel?: string;
17
+ onSubmit?: (value: string[]) => void;
18
+ onCancel?: () => void;
19
+ onCleanup?: () => void;
20
+ }): ReactElement;
@@ -0,0 +1,132 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useState } from "react";
3
+ import { createRoot } from "react-dom/client";
4
+ import { useForm } from "@tanstack/react-form";
5
+ import {
6
+ Dialog,
7
+ DialogContent,
8
+ DialogDescription,
9
+ DialogFooter,
10
+ DialogHeader,
11
+ DialogTitle
12
+ } from "../components/ui/dialog";
13
+ import { Button } from "../components/ui/button";
14
+ import { SelectUsers } from "./select-users";
15
+ function showSelectUsersDialog({
16
+ projectEmails,
17
+ initialValue = [],
18
+ title = "Select users",
19
+ description = "Choose one or more project users.",
20
+ confirmLabel = "Apply",
21
+ cancelLabel = "Cancel"
22
+ }) {
23
+ return new Promise((resolve) => {
24
+ const container = document.createElement("div");
25
+ document.body.appendChild(container);
26
+ const root = createRoot(container);
27
+ const cleanup = () => {
28
+ root.unmount();
29
+ container.remove();
30
+ };
31
+ root.render(
32
+ /* @__PURE__ */ jsx(
33
+ SelectUsersDialog,
34
+ {
35
+ projectEmails,
36
+ initialValue,
37
+ title,
38
+ description,
39
+ confirmLabel,
40
+ cancelLabel,
41
+ onSubmit: resolve,
42
+ onCancel: () => resolve(null),
43
+ onCleanup: cleanup
44
+ }
45
+ )
46
+ );
47
+ });
48
+ }
49
+ function SelectUsersDialog({
50
+ projectEmails,
51
+ initialValue = [],
52
+ title = "Select users",
53
+ description = "Choose one or more project users.",
54
+ confirmLabel = "Apply",
55
+ cancelLabel = "Cancel",
56
+ onSubmit,
57
+ onCancel,
58
+ onCleanup
59
+ }) {
60
+ const [open, setOpen] = useState(true);
61
+ const form = useForm({
62
+ defaultValues: {
63
+ users: initialValue
64
+ },
65
+ onSubmit: ({ value }) => {
66
+ onSubmit?.(value.users);
67
+ setOpen(false);
68
+ }
69
+ });
70
+ const close = useCallback(() => {
71
+ onCancel?.();
72
+ setOpen(false);
73
+ }, [onCancel]);
74
+ return /* @__PURE__ */ jsx(
75
+ Dialog,
76
+ {
77
+ open,
78
+ onOpenChange: (nextOpen) => {
79
+ if (!nextOpen) {
80
+ close();
81
+ }
82
+ },
83
+ children: /* @__PURE__ */ jsxs(
84
+ DialogContent,
85
+ {
86
+ className: "max-w-[min(92vw,560px)]",
87
+ onAnimationEnd: () => {
88
+ if (!open) {
89
+ onCleanup?.();
90
+ }
91
+ },
92
+ children: [
93
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
94
+ /* @__PURE__ */ jsx(DialogTitle, { children: title }),
95
+ /* @__PURE__ */ jsx(DialogDescription, { children: description })
96
+ ] }),
97
+ /* @__PURE__ */ jsxs(
98
+ "form",
99
+ {
100
+ className: "grid gap-4",
101
+ onSubmit: (event) => {
102
+ event.preventDefault();
103
+ event.stopPropagation();
104
+ void form.handleSubmit();
105
+ },
106
+ children: [
107
+ /* @__PURE__ */ jsx(form.Field, { name: "users", children: (field) => /* @__PURE__ */ jsx(
108
+ SelectUsers,
109
+ {
110
+ autoFocus: true,
111
+ projectEmails,
112
+ value: field.state.value,
113
+ onChanged: (value) => field.handleChange(value)
114
+ }
115
+ ) }),
116
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
117
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: close, children: cancelLabel }),
118
+ /* @__PURE__ */ jsx(Button, { type: "submit", children: confirmLabel })
119
+ ] })
120
+ ]
121
+ }
122
+ )
123
+ ]
124
+ }
125
+ )
126
+ }
127
+ );
128
+ }
129
+ export {
130
+ SelectUsersDialog,
131
+ showSelectUsersDialog
132
+ };
@@ -0,0 +1,16 @@
1
+ import type { ReactElement } from "react";
2
+ import { MultiSelectController } from "./multi-select-autocomplete";
3
+ export declare class SelectUsersController extends MultiSelectController {
4
+ static readonly emailRegex: RegExp;
5
+ canAddItem(item: string): boolean;
6
+ }
7
+ export declare function SelectUsers({ projectEmails, onChanged, controller, autoFocus, initialValue, value, className, }: {
8
+ projectEmails: string[];
9
+ onChanged?: (value: string[]) => void;
10
+ controller?: SelectUsersController;
11
+ autoFocus?: boolean;
12
+ initialValue?: string[];
13
+ value?: string[];
14
+ className?: string;
15
+ }): ReactElement;
16
+ export declare function buildSelectedUsersResult(selected: readonly string[], pendingEmail: string): string[];
@@ -0,0 +1,97 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo } from "react";
3
+ import { parseEmailList } from "./email-address";
4
+ import { MultiSelectAutocomplete, MultiSelectController } from "./multi-select-autocomplete";
5
+ class SelectUsersController extends MultiSelectController {
6
+ static emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/u;
7
+ canAddItem(item) {
8
+ return SelectUsersController.emailRegex.test(item);
9
+ }
10
+ }
11
+ function selectedIncludes(selected, email) {
12
+ const normalized = email.toLowerCase();
13
+ return selected.some((item) => item.toLowerCase() === normalized);
14
+ }
15
+ function SelectUsers({
16
+ projectEmails,
17
+ onChanged,
18
+ controller,
19
+ autoFocus = false,
20
+ initialValue = [],
21
+ value,
22
+ className
23
+ }) {
24
+ const effectiveController = useMemo(
25
+ () => controller ?? new SelectUsersController(initialValue),
26
+ [controller]
27
+ );
28
+ useEffect(() => {
29
+ if (controller != null) {
30
+ return void 0;
31
+ }
32
+ return () => {
33
+ effectiveController.dispose();
34
+ };
35
+ }, [controller, effectiveController]);
36
+ const search = useCallback((query) => {
37
+ if (query.trim() === "") {
38
+ return projectEmails;
39
+ }
40
+ const lower = query.toLowerCase();
41
+ return projectEmails.filter((email) => email.toLowerCase().includes(lower));
42
+ }, [projectEmails]);
43
+ return /* @__PURE__ */ jsx(
44
+ MultiSelectAutocomplete,
45
+ {
46
+ search,
47
+ controller: effectiveController,
48
+ onChanged,
49
+ initialValue,
50
+ value,
51
+ autoFocus,
52
+ placeholder: "Type an email",
53
+ minimumSearchLength: 1,
54
+ className,
55
+ onTextChanged: (text, { add, setText }) => {
56
+ if (text.trim() === "") {
57
+ return;
58
+ }
59
+ const parsed = parseEmailList(text);
60
+ if (parsed.length === 0) {
61
+ return;
62
+ }
63
+ if (parsed.length === 1) {
64
+ if (text.endsWith(" ") || text.endsWith(",")) {
65
+ const email = parsed[0]?.sanitizedAddress.trim() ?? "";
66
+ if (SelectUsersController.emailRegex.test(email)) {
67
+ void add(email);
68
+ setText("");
69
+ }
70
+ }
71
+ return;
72
+ }
73
+ for (const address of parsed.slice(0, -1)) {
74
+ const email = address.sanitizedAddress.trim();
75
+ if (SelectUsersController.emailRegex.test(email)) {
76
+ void add(email);
77
+ }
78
+ }
79
+ const remainder = parsed[parsed.length - 1]?.sanitizedAddress.trim() ?? "";
80
+ setText(remainder);
81
+ }
82
+ }
83
+ );
84
+ }
85
+ function buildSelectedUsersResult(selected, pendingEmail) {
86
+ const result = [...selected];
87
+ const trimmed = pendingEmail.trim();
88
+ if (SelectUsersController.emailRegex.test(trimmed) && !selectedIncludes(result, trimmed)) {
89
+ result.push(trimmed);
90
+ }
91
+ return result;
92
+ }
93
+ export {
94
+ SelectUsers,
95
+ SelectUsersController,
96
+ buildSelectedUsersResult
97
+ };
@@ -1,11 +1,19 @@
1
- export * from './Chat';
2
- export * from './ChatBotView';
3
- export * from './ChatInput';
4
- export * from './ChatThread';
5
- export * from './ChatTypingIndicator';
6
- export * from './FileUploader';
7
- export * from './chat-message';
8
- export * from './conversation-descriptor';
9
- export * from './file-attachment';
10
- export * from './multi-thread-view';
11
- export * from './chat-hooks';
1
+ export * from './chat/chat-bot-view';
2
+ export * from './chat/chat-hooks';
3
+ export * from './chat/chat-input';
4
+ export * from './chat/chat-message';
5
+ export * from './chat/chat-thread';
6
+ export * from './chat/chat-typing-indicator';
7
+ export * from './chat/conversation-descriptor';
8
+ export * from './chat/dataset-chat-thread';
9
+ export * from './chat/file-attachment';
10
+ export * from './chat/file-uploader';
11
+ export * from './chat/multi-thread-view';
12
+ export * from './chat/new-chat-thread';
13
+ export * from './file-preview/file-preview';
14
+ export * from './forms/email-address';
15
+ export * from './forms/form';
16
+ export * from './forms/multi-select-autocomplete';
17
+ export * from './forms/select-users';
18
+ export * from './forms/select-users-dialog';
19
+ export * from './meetings/meetings';
package/dist/esm/index.js CHANGED
@@ -1,11 +1,19 @@
1
- export * from "./Chat";
2
- export * from "./ChatBotView";
3
- export * from "./ChatInput";
4
- export * from "./ChatThread";
5
- export * from "./ChatTypingIndicator";
6
- export * from "./FileUploader";
7
- export * from "./chat-message";
8
- export * from "./conversation-descriptor";
9
- export * from "./file-attachment";
10
- export * from "./multi-thread-view";
11
- export * from "./chat-hooks";
1
+ export * from "./chat/chat-bot-view";
2
+ export * from "./chat/chat-hooks";
3
+ export * from "./chat/chat-input";
4
+ export * from "./chat/chat-message";
5
+ export * from "./chat/chat-thread";
6
+ export * from "./chat/chat-typing-indicator";
7
+ export * from "./chat/conversation-descriptor";
8
+ export * from "./chat/dataset-chat-thread";
9
+ export * from "./chat/file-attachment";
10
+ export * from "./chat/file-uploader";
11
+ export * from "./chat/multi-thread-view";
12
+ export * from "./chat/new-chat-thread";
13
+ export * from "./file-preview/file-preview";
14
+ export * from "./forms/email-address";
15
+ export * from "./forms/form";
16
+ export * from "./forms/multi-select-autocomplete";
17
+ export * from "./forms/select-users";
18
+ export * from "./forms/select-users-dialog";
19
+ export * from "./meetings/meetings";
@@ -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,54 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState, useSyncExternalStore } from "react";
3
+ function useParticipantSnapshot(participant) {
4
+ useSyncExternalStore(
5
+ (listener) => {
6
+ const events = ["isSpeakingChanged", "attributesChanged", "trackMuted", "trackUnmuted"];
7
+ for (const eventName of events) {
8
+ participant.on(eventName, listener);
9
+ }
10
+ return () => {
11
+ for (const eventName of events) {
12
+ participant.off(eventName, listener);
13
+ }
14
+ };
15
+ },
16
+ () => `${participant.isSpeaking}:${participant.audioLevel}:${participant.attributes["lk.agent.state"] ?? ""}`,
17
+ () => ""
18
+ );
19
+ }
20
+ function AudioWave({
21
+ room: _room,
22
+ participant,
23
+ className
24
+ }) {
25
+ useParticipantSnapshot(participant);
26
+ const [, setFrame] = useState(0);
27
+ const bars = useRef(Array.from({ length: 28 }, (_, index) => 0.25 + index % 7 * 0.08));
28
+ const thinking = participant.attributes["lk.agent.state"] === "thinking";
29
+ const amplitude = thinking ? 0.2 : Math.max(participant.audioLevel, participant.isSpeaking ? 0.45 : 0.12);
30
+ useEffect(() => {
31
+ const interval = window.setInterval(() => {
32
+ bars.current = bars.current.map((value, index) => {
33
+ const phase = Date.now() / 180 + index * 0.65;
34
+ return Math.max(0.12, Math.min(1, value * 0.6 + Math.abs(Math.sin(phase)) * amplitude * 0.8));
35
+ });
36
+ setFrame((frame) => frame + 1);
37
+ }, 1e3 / 30);
38
+ return () => window.clearInterval(interval);
39
+ }, [amplitude]);
40
+ return /* @__PURE__ */ jsx("div", { className: className ?? "flex h-full w-full items-center justify-center bg-background", children: /* @__PURE__ */ 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__ */ jsx(
41
+ "div",
42
+ {
43
+ className: "w-1 rounded-full bg-foreground/30 transition-[height,background-color]",
44
+ style: {
45
+ height: `${Math.max(10, height * 96)}px`,
46
+ backgroundColor: participant.isSpeaking ? "hsl(var(--foreground) / 0.42)" : "hsl(var(--foreground) / 0.18)"
47
+ }
48
+ },
49
+ index
50
+ )) }) });
51
+ }
52
+ export {
53
+ AudioWave
54
+ };
@@ -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;