@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,316 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useRef, useState } from "react";
3
+ import { Download, FileText } from "lucide-react";
4
+ import { Document, Page, pdfjs } from "react-pdf";
5
+ import {
6
+ Dialog,
7
+ DialogContent,
8
+ DialogDescription,
9
+ DialogHeader,
10
+ DialogTitle,
11
+ DialogTrigger
12
+ } from "../components/ui/dialog";
13
+ import { Button } from "../components/ui/button";
14
+ import { Spinner } from "../components/ui/spinner";
15
+ import { cn } from "../lib/utils";
16
+ pdfjs.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
17
+ var FileKind = /* @__PURE__ */ ((FileKind2) => {
18
+ FileKind2["Image"] = "image";
19
+ FileKind2["Video"] = "video";
20
+ FileKind2["Pdf"] = "pdf";
21
+ FileKind2["Unknown"] = "unknown";
22
+ return FileKind2;
23
+ })(FileKind || {});
24
+ const imageExtensions = /* @__PURE__ */ new Set([
25
+ "avif",
26
+ "bmp",
27
+ "gif",
28
+ "heic",
29
+ "heif",
30
+ "jfif",
31
+ "jpeg",
32
+ "jpg",
33
+ "png",
34
+ "svg",
35
+ "svgz",
36
+ "tif",
37
+ "tiff",
38
+ "webp"
39
+ ]);
40
+ const videoExtensions = /* @__PURE__ */ new Set(["m4v", "mkv", "mov", "mp4", "webm"]);
41
+ const pdfExtensions = /* @__PURE__ */ new Set(["pdf"]);
42
+ function basename(path) {
43
+ const withoutQuery = path.split("?")[0] ?? path;
44
+ const withoutHash = withoutQuery.split("#")[0] ?? withoutQuery;
45
+ return withoutHash.split("/").pop() ?? withoutHash;
46
+ }
47
+ function extension(path) {
48
+ const name = basename(path).trim();
49
+ const dotIndex = name.lastIndexOf(".");
50
+ if (dotIndex < 0 || dotIndex === name.length - 1) {
51
+ return "";
52
+ }
53
+ return name.slice(dotIndex + 1).toLowerCase();
54
+ }
55
+ function isHttpUrl(path) {
56
+ return /^https?:\/\//iu.test(path.trim());
57
+ }
58
+ function filePreviewName(path) {
59
+ const name = basename(path).trim();
60
+ return name === "" ? "Attachment" : name;
61
+ }
62
+ function classifyFile(path) {
63
+ const ext = extension(path);
64
+ if (imageExtensions.has(ext)) {
65
+ return "image" /* Image */;
66
+ }
67
+ if (videoExtensions.has(ext)) {
68
+ return "video" /* Video */;
69
+ }
70
+ if (pdfExtensions.has(ext)) {
71
+ return "pdf" /* Pdf */;
72
+ }
73
+ return "unknown" /* Unknown */;
74
+ }
75
+ function isImagePath(path) {
76
+ return classifyFile(path) === "image" /* Image */;
77
+ }
78
+ function useDownloadUrl(room, path) {
79
+ const [url, setUrl] = useState(null);
80
+ const [error, setError] = useState(null);
81
+ useEffect(() => {
82
+ let cancelled = false;
83
+ const normalizedPath = path.trim();
84
+ setUrl(null);
85
+ setError(null);
86
+ if (normalizedPath === "") {
87
+ return;
88
+ }
89
+ if (isHttpUrl(normalizedPath)) {
90
+ setUrl(normalizedPath);
91
+ return;
92
+ }
93
+ void room.storage.downloadUrl(normalizedPath).then((nextUrl) => {
94
+ if (!cancelled) {
95
+ setUrl(nextUrl);
96
+ }
97
+ }).catch((nextError) => {
98
+ if (!cancelled) {
99
+ setError(nextError);
100
+ }
101
+ });
102
+ return () => {
103
+ cancelled = true;
104
+ };
105
+ }, [path, room]);
106
+ return { url, error };
107
+ }
108
+ function usePdfUrl(room, path) {
109
+ const [url, setUrl] = useState(null);
110
+ const [error, setError] = useState(null);
111
+ useEffect(() => {
112
+ let cancelled = false;
113
+ let objectUrl = null;
114
+ const normalizedPath = path.trim();
115
+ setUrl(null);
116
+ setError(null);
117
+ if (normalizedPath === "") {
118
+ return;
119
+ }
120
+ if (isHttpUrl(normalizedPath)) {
121
+ setUrl(normalizedPath);
122
+ return;
123
+ }
124
+ void room.storage.download(normalizedPath).then((content) => {
125
+ if (cancelled) {
126
+ return;
127
+ }
128
+ objectUrl = URL.createObjectURL(new Blob([content.data], { type: "application/pdf" }));
129
+ setUrl(objectUrl);
130
+ }).catch((nextError) => {
131
+ if (!cancelled) {
132
+ setError(nextError);
133
+ }
134
+ });
135
+ return () => {
136
+ cancelled = true;
137
+ if (objectUrl != null) {
138
+ URL.revokeObjectURL(objectUrl);
139
+ }
140
+ };
141
+ }, [path, room]);
142
+ return { url, error };
143
+ }
144
+ function ErrorPreview({ message }) {
145
+ return /* @__PURE__ */ jsx("div", { className: "flex h-full min-h-48 items-center justify-center p-6 text-center text-sm text-destructive", children: message });
146
+ }
147
+ function LoadingPreview() {
148
+ return /* @__PURE__ */ jsx("div", { className: "flex h-full min-h-48 items-center justify-center", children: /* @__PURE__ */ jsx(Spinner, { className: "h-6 w-6" }) });
149
+ }
150
+ function DownloadButton({ room, path }) {
151
+ const { url } = useDownloadUrl(room, path);
152
+ return /* @__PURE__ */ jsxs(
153
+ Button,
154
+ {
155
+ type: "button",
156
+ variant: "outline",
157
+ size: "sm",
158
+ disabled: url == null,
159
+ onClick: () => {
160
+ if (url != null) {
161
+ window.open(url, "_blank", "noopener,noreferrer");
162
+ }
163
+ },
164
+ children: [
165
+ /* @__PURE__ */ jsx(Download, { className: "h-4 w-4" }),
166
+ "Download"
167
+ ]
168
+ }
169
+ );
170
+ }
171
+ function ImagePreview({
172
+ room,
173
+ path,
174
+ alt = filePreviewName(path)
175
+ }) {
176
+ const { url, error } = useDownloadUrl(room, path);
177
+ if (error != null) {
178
+ return /* @__PURE__ */ jsx(ErrorPreview, { message: `Unable to load image preview: ${String(error)}` });
179
+ }
180
+ if (url == null) {
181
+ return /* @__PURE__ */ jsx(LoadingPreview, {});
182
+ }
183
+ return /* @__PURE__ */ jsx("img", { src: url, alt, className: "h-full max-h-full w-full object-contain" });
184
+ }
185
+ function VideoPreview({ room, path }) {
186
+ const { url, error } = useDownloadUrl(room, path);
187
+ if (error != null) {
188
+ return /* @__PURE__ */ jsx(ErrorPreview, { message: `Unable to load video preview: ${String(error)}` });
189
+ }
190
+ if (url == null) {
191
+ return /* @__PURE__ */ jsx(LoadingPreview, {});
192
+ }
193
+ return /* @__PURE__ */ jsx(
194
+ "video",
195
+ {
196
+ src: url,
197
+ controls: true,
198
+ playsInline: true,
199
+ className: "h-full max-h-full w-full bg-black object-contain"
200
+ }
201
+ );
202
+ }
203
+ function useElementWidth() {
204
+ const ref = useRef(null);
205
+ const [width, setWidth] = useState(0);
206
+ useEffect(() => {
207
+ const element = ref.current;
208
+ if (element == null) {
209
+ return;
210
+ }
211
+ const updateWidth = () => {
212
+ setWidth(element.clientWidth);
213
+ };
214
+ updateWidth();
215
+ const observer = new ResizeObserver(updateWidth);
216
+ observer.observe(element);
217
+ return () => {
218
+ observer.disconnect();
219
+ };
220
+ }, []);
221
+ return [ref, width];
222
+ }
223
+ function PdfPreview({ room, path }) {
224
+ const { url, error } = usePdfUrl(room, path);
225
+ const [numPages, setNumPages] = useState(0);
226
+ const [containerRef, containerWidth] = useElementWidth();
227
+ const pageWidth = Math.max(Math.min(containerWidth, 960), 320);
228
+ if (error != null) {
229
+ return /* @__PURE__ */ jsx(ErrorPreview, { message: `Unable to load PDF: ${String(error)}` });
230
+ }
231
+ return /* @__PURE__ */ jsx("div", { ref: containerRef, className: "h-full overflow-auto bg-muted/30 p-4", children: url == null ? /* @__PURE__ */ jsx(LoadingPreview, {}) : /* @__PURE__ */ jsx(
232
+ Document,
233
+ {
234
+ file: url,
235
+ loading: /* @__PURE__ */ jsx(LoadingPreview, {}),
236
+ error: /* @__PURE__ */ jsx(ErrorPreview, { message: "Unable to render PDF preview." }),
237
+ onLoadSuccess: ({ numPages: nextNumPages }) => {
238
+ setNumPages(nextNumPages);
239
+ },
240
+ children: /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center gap-4", children: Array.from({ length: numPages }, (_, index) => /* @__PURE__ */ jsx(
241
+ Page,
242
+ {
243
+ pageNumber: index + 1,
244
+ width: pageWidth,
245
+ renderAnnotationLayer: false,
246
+ renderTextLayer: false,
247
+ loading: /* @__PURE__ */ jsx("div", { className: "h-40" }),
248
+ className: "overflow-hidden rounded-md bg-background shadow-sm"
249
+ },
250
+ `page_${index + 1}`
251
+ )) })
252
+ }
253
+ ) });
254
+ }
255
+ function UnsupportedPreview({ room, path }) {
256
+ const filename = filePreviewName(path);
257
+ return /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-64 flex-col items-center justify-center gap-4 p-8 text-center", children: [
258
+ /* @__PURE__ */ jsx("div", { className: "flex h-14 w-14 items-center justify-center rounded-md border bg-muted/50", children: /* @__PURE__ */ jsx(FileText, { className: "h-7 w-7 text-muted-foreground" }) }),
259
+ /* @__PURE__ */ jsxs("div", { className: "max-w-md space-y-1", children: [
260
+ /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium text-foreground", children: filename }),
261
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: "No preview available for this file type." })
262
+ ] }),
263
+ /* @__PURE__ */ jsx(DownloadButton, { room, path })
264
+ ] });
265
+ }
266
+ function FilePreview({ room, path }) {
267
+ const kind = useMemo(() => classifyFile(path), [path]);
268
+ switch (kind) {
269
+ case "image" /* Image */:
270
+ return /* @__PURE__ */ jsx(ImagePreview, { room, path });
271
+ case "video" /* Video */:
272
+ return /* @__PURE__ */ jsx(VideoPreview, { room, path });
273
+ case "pdf" /* Pdf */:
274
+ return /* @__PURE__ */ jsx(PdfPreview, { room, path });
275
+ case "unknown" /* Unknown */:
276
+ return /* @__PURE__ */ jsx(UnsupportedPreview, { room, path });
277
+ }
278
+ }
279
+ function FilePreviewDialog({
280
+ room,
281
+ path,
282
+ children,
283
+ className
284
+ }) {
285
+ const filename = filePreviewName(path);
286
+ return /* @__PURE__ */ jsxs(Dialog, { children: [
287
+ /* @__PURE__ */ jsx(DialogTrigger, { asChild: true, children }),
288
+ /* @__PURE__ */ jsxs(
289
+ DialogContent,
290
+ {
291
+ className: cn(
292
+ "h-[min(90vh,900px)] max-w-[min(96vw,1100px)] grid-rows-[auto_minmax(0,1fr)] gap-0 p-0",
293
+ className
294
+ ),
295
+ children: [
296
+ /* @__PURE__ */ jsxs(DialogHeader, { className: "border-b px-4 py-3 pr-12", children: [
297
+ /* @__PURE__ */ jsx(DialogTitle, { className: "truncate text-base", children: filename }),
298
+ /* @__PURE__ */ jsx(DialogDescription, { className: "sr-only", children: "File preview" })
299
+ ] }),
300
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-hidden", children: /* @__PURE__ */ jsx(FilePreview, { room, path }) })
301
+ ]
302
+ }
303
+ )
304
+ ] });
305
+ }
306
+ export {
307
+ FileKind,
308
+ FilePreview,
309
+ FilePreviewDialog,
310
+ ImagePreview,
311
+ PdfPreview,
312
+ VideoPreview,
313
+ classifyFile,
314
+ filePreviewName,
315
+ isImagePath
316
+ };
@@ -0,0 +1,10 @@
1
+ export declare class Address {
2
+ readonly mailAddress: string;
3
+ readonly name: string | null;
4
+ private static readonly quotableNameRegExp;
5
+ constructor(mailAddress: string, name?: string | null);
6
+ get sanitizedName(): string | null;
7
+ get sanitizedAddress(): string;
8
+ toString(): string;
9
+ }
10
+ export declare function parseEmailList(addresses: string): Address[];
@@ -0,0 +1,85 @@
1
+ class Address {
2
+ mailAddress;
3
+ name;
4
+ static quotableNameRegExp = /[",]/u;
5
+ constructor(mailAddress, name = null) {
6
+ this.mailAddress = mailAddress;
7
+ this.name = name;
8
+ }
9
+ get sanitizedName() {
10
+ if (this.name == null) {
11
+ return null;
12
+ }
13
+ if (Address.quotableNameRegExp.test(this.name)) {
14
+ return `"${this.name.replace(/"/gu, '\\"')}"`;
15
+ }
16
+ return this.name;
17
+ }
18
+ get sanitizedAddress() {
19
+ return this.mailAddress;
20
+ }
21
+ toString() {
22
+ return this.name == null ? this.mailAddress : `${this.name} <${this.mailAddress}>`;
23
+ }
24
+ }
25
+ function parseEmailList(addresses) {
26
+ const result = [];
27
+ const nameOrEmail = [];
28
+ const email = [];
29
+ const name = [];
30
+ let inQuote = false;
31
+ let inAngleBrackets = false;
32
+ const addAddress = () => {
33
+ if (nameOrEmail.length > 0) {
34
+ if (email.length === 0) {
35
+ email.push(...nameOrEmail);
36
+ } else if (name.length === 0) {
37
+ name.push(...nameOrEmail);
38
+ }
39
+ }
40
+ if (email.length > 0) {
41
+ const parsedName = name.join("").trim();
42
+ result.push(new Address(email.join("").trim(), parsedName === "" ? null : parsedName));
43
+ }
44
+ email.length = 0;
45
+ name.length = 0;
46
+ nameOrEmail.length = 0;
47
+ inAngleBrackets = false;
48
+ inQuote = false;
49
+ };
50
+ for (let index = 0; index < addresses.length; index += 1) {
51
+ const char = addresses[index];
52
+ if (inQuote) {
53
+ if (char === '"') {
54
+ inQuote = false;
55
+ } else if (char === "\\") {
56
+ index += 1;
57
+ if (index < addresses.length) {
58
+ name.push(addresses[index] ?? "");
59
+ }
60
+ } else if (char != null) {
61
+ name.push(char);
62
+ }
63
+ } else if (inAngleBrackets) {
64
+ if (char === ">") {
65
+ inAngleBrackets = false;
66
+ } else if (char != null) {
67
+ email.push(char);
68
+ }
69
+ } else if (char === "," || char === ";") {
70
+ addAddress();
71
+ } else if (char === '"') {
72
+ inQuote = true;
73
+ } else if (char === "<") {
74
+ inAngleBrackets = true;
75
+ } else if (char != null) {
76
+ nameOrEmail.push(char);
77
+ }
78
+ }
79
+ addAddress();
80
+ return result;
81
+ }
82
+ export {
83
+ Address,
84
+ parseEmailList
85
+ };
@@ -0,0 +1,27 @@
1
+ import type { ReactElement } from "react";
2
+ import { Element as MeshElement, MeshDocument, RoomClient } from "@meshagent/meshagent";
3
+ export type FormDocumentValue = string;
4
+ export type FormDocumentValues = Record<string, FormDocumentValue>;
5
+ type FormDocumentForm = any;
6
+ export declare function FormDocumentViewer({ document, client: _client, onSubmit, submitLabel, }: {
7
+ client?: RoomClient;
8
+ document: MeshDocument;
9
+ onSubmit?: (values: FormDocumentValues) => void;
10
+ submitLabel?: string;
11
+ }): ReactElement | null;
12
+ export declare function FormDocumentField({ element, name, form, }: {
13
+ element: MeshElement;
14
+ name: string;
15
+ form: FormDocumentForm;
16
+ }): ReactElement;
17
+ export declare function FormDocumentSelect({ element, name, form, }: {
18
+ element: MeshElement;
19
+ name: string;
20
+ form: FormDocumentForm;
21
+ }): ReactElement;
22
+ export declare function FormDocumentInput({ element, name, form, }: {
23
+ element: MeshElement;
24
+ name: string;
25
+ form: FormDocumentForm;
26
+ }): ReactElement;
27
+ export {};
@@ -0,0 +1,193 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useState } from "react";
3
+ import { Element as MeshElement } from "@meshagent/meshagent";
4
+ import { useForm } from "@tanstack/react-form";
5
+ import { Button } from "../components/ui/button";
6
+ import {
7
+ Card,
8
+ CardContent,
9
+ CardDescription,
10
+ CardFooter,
11
+ CardHeader,
12
+ CardTitle
13
+ } from "../components/ui/card";
14
+ import { Input } from "../components/ui/input";
15
+ import { Label } from "../components/ui/label";
16
+ import {
17
+ Select,
18
+ SelectContent,
19
+ SelectItem,
20
+ SelectTrigger,
21
+ SelectValue
22
+ } from "../components/ui/select";
23
+ import { Textarea } from "../components/ui/textarea";
24
+ function stringAttribute(element, name) {
25
+ const value = element.getAttribute(name);
26
+ return typeof value === "string" ? value : null;
27
+ }
28
+ function fieldName(element, index) {
29
+ return stringAttribute(element, "name") ?? stringAttribute(element, "$id") ?? stringAttribute(element, "id") ?? `${element.tagName}_${index}`;
30
+ }
31
+ function fieldDefaultValue(element) {
32
+ return stringAttribute(element, "value") ?? stringAttribute(element, "default_value") ?? stringAttribute(element, "defaultValue") ?? "";
33
+ }
34
+ function elementChildren(element) {
35
+ return element.getChildren().filter((child) => child instanceof MeshElement);
36
+ }
37
+ function formFields(document) {
38
+ return elementChildren(document.root);
39
+ }
40
+ function formDefaultValues(document) {
41
+ const values = {};
42
+ formFields(document).forEach((field, index) => {
43
+ values[fieldName(field, index)] = fieldDefaultValue(field);
44
+ });
45
+ return values;
46
+ }
47
+ function useDocumentVersion(document) {
48
+ const [version, setVersion] = useState(0);
49
+ useEffect(() => {
50
+ const onUpdated = () => setVersion((current) => current + 1);
51
+ document.on("updated", onUpdated);
52
+ return () => {
53
+ document.off("updated", onUpdated);
54
+ };
55
+ }, [document]);
56
+ return version;
57
+ }
58
+ function FieldText({
59
+ element,
60
+ fallbackName
61
+ }) {
62
+ return {
63
+ name: fallbackName,
64
+ label: stringAttribute(element, "label"),
65
+ description: stringAttribute(element, "description")
66
+ };
67
+ }
68
+ function FormDocumentViewer({
69
+ document,
70
+ client: _client,
71
+ onSubmit,
72
+ submitLabel = "Submit"
73
+ }) {
74
+ const version = useDocumentVersion(document);
75
+ const fields = useMemo(() => formFields(document), [document, version]);
76
+ const defaultValues = useMemo(() => formDefaultValues(document), [document, version]);
77
+ const title = stringAttribute(document.root, "title");
78
+ const description = stringAttribute(document.root, "description");
79
+ const form = useForm({
80
+ defaultValues,
81
+ onSubmit: ({ value }) => {
82
+ onSubmit?.(value);
83
+ }
84
+ });
85
+ useEffect(() => {
86
+ form.reset(defaultValues);
87
+ }, [defaultValues, form]);
88
+ if (fields.length === 0) {
89
+ return null;
90
+ }
91
+ return /* @__PURE__ */ jsx("div", { className: "px-4", children: /* @__PURE__ */ jsxs(Card, { children: [
92
+ title != null || description != null ? /* @__PURE__ */ jsxs(CardHeader, { children: [
93
+ title != null ? /* @__PURE__ */ jsx(CardTitle, { children: title }) : null,
94
+ description != null ? /* @__PURE__ */ jsx(CardDescription, { children: description }) : null
95
+ ] }) : null,
96
+ /* @__PURE__ */ jsxs(
97
+ "form",
98
+ {
99
+ onSubmit: (event) => {
100
+ event.preventDefault();
101
+ event.stopPropagation();
102
+ void form.handleSubmit();
103
+ },
104
+ children: [
105
+ /* @__PURE__ */ jsx(CardContent, { className: "grid gap-6", children: fields.map((field, index) => /* @__PURE__ */ jsx(
106
+ FormDocumentField,
107
+ {
108
+ element: field,
109
+ name: fieldName(field, index),
110
+ form
111
+ },
112
+ field.id ?? `${field.tagName}:${index}`
113
+ )) }),
114
+ onSubmit != null ? /* @__PURE__ */ jsx(CardFooter, { className: "justify-end pt-6", children: /* @__PURE__ */ jsx(Button, { type: "submit", children: submitLabel }) }) : null
115
+ ]
116
+ }
117
+ )
118
+ ] }) });
119
+ }
120
+ function FormDocumentField({
121
+ element,
122
+ name,
123
+ form
124
+ }) {
125
+ if (element.tagName === "select") {
126
+ return /* @__PURE__ */ jsx(FormDocumentSelect, { element, name, form });
127
+ }
128
+ if (element.tagName === "input") {
129
+ return /* @__PURE__ */ jsx(FormDocumentInput, { element, name, form });
130
+ }
131
+ throw new Error("Unexpected form field type");
132
+ }
133
+ function FormDocumentSelect({
134
+ element,
135
+ name,
136
+ form
137
+ }) {
138
+ const text = FieldText({ element, fallbackName: name });
139
+ const options = elementChildren(element).map((option) => ({
140
+ value: stringAttribute(option, "value") ?? "",
141
+ text: stringAttribute(option, "text") ?? stringAttribute(option, "value") ?? ""
142
+ }));
143
+ return /* @__PURE__ */ jsx(form.Field, { name, children: (field) => /* @__PURE__ */ jsxs("div", { className: "grid gap-2", children: [
144
+ text.label != null ? /* @__PURE__ */ jsx(Label, { htmlFor: field.name, children: text.label }) : null,
145
+ /* @__PURE__ */ jsxs(
146
+ Select,
147
+ {
148
+ value: field.state.value,
149
+ onValueChange: (value) => field.handleChange(value),
150
+ children: [
151
+ /* @__PURE__ */ jsx(SelectTrigger, { id: field.name, className: "w-full", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "pick a value" }) }),
152
+ /* @__PURE__ */ jsx(SelectContent, { children: options.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option.value, children: option.text }, option.value)) })
153
+ ]
154
+ }
155
+ ),
156
+ text.description != null ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: text.description }) : null
157
+ ] }) });
158
+ }
159
+ function FormDocumentInput({
160
+ element,
161
+ name,
162
+ form
163
+ }) {
164
+ const text = FieldText({ element, fallbackName: name });
165
+ const multiline = element.getAttribute("multiline") === true || stringAttribute(element, "multiline") === "true";
166
+ return /* @__PURE__ */ jsx(form.Field, { name, children: (field) => /* @__PURE__ */ jsxs("div", { className: "grid gap-2", children: [
167
+ text.label != null ? /* @__PURE__ */ jsx(Label, { htmlFor: field.name, children: text.label }) : null,
168
+ multiline ? /* @__PURE__ */ jsx(
169
+ Textarea,
170
+ {
171
+ id: field.name,
172
+ value: field.state.value,
173
+ onBlur: field.handleBlur,
174
+ onChange: (event) => field.handleChange(event.currentTarget.value)
175
+ }
176
+ ) : /* @__PURE__ */ jsx(
177
+ Input,
178
+ {
179
+ id: field.name,
180
+ value: field.state.value,
181
+ onBlur: field.handleBlur,
182
+ onChange: (event) => field.handleChange(event.currentTarget.value)
183
+ }
184
+ ),
185
+ text.description != null ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: text.description }) : null
186
+ ] }) });
187
+ }
188
+ export {
189
+ FormDocumentField,
190
+ FormDocumentInput,
191
+ FormDocumentSelect,
192
+ FormDocumentViewer
193
+ };
@@ -0,0 +1,35 @@
1
+ import type { ReactElement } from "react";
2
+ export type AsyncSearch = (query: string) => Promise<string[]> | string[];
3
+ export declare class MultiSelectController {
4
+ private listeners;
5
+ private _value;
6
+ constructor(initialValue?: string[]);
7
+ get value(): string[];
8
+ set value(nextValue: string[]);
9
+ add(item: string): Promise<boolean>;
10
+ canAddItem(_item: string): boolean | Promise<boolean>;
11
+ remove(item: string): void;
12
+ removeLast(): void;
13
+ clear(): void;
14
+ subscribe(listener: (value: string[]) => void): () => void;
15
+ dispose(): void;
16
+ private notify;
17
+ }
18
+ export declare function MultiSelectAutocomplete({ search, onChanged, controller, placeholder, debounceDuration, minimumSearchLength, initialValue, value, autoFocus, className, inputClassName, onTextChanged, }: {
19
+ search: AsyncSearch;
20
+ onChanged?: (value: string[]) => void;
21
+ controller?: MultiSelectController;
22
+ placeholder?: string;
23
+ debounceDuration?: number;
24
+ minimumSearchLength?: number;
25
+ initialValue?: string[];
26
+ value?: string[];
27
+ autoFocus?: boolean;
28
+ className?: string;
29
+ inputClassName?: string;
30
+ onTextChanged?: (text: string, context: {
31
+ add: (item: string) => Promise<void>;
32
+ setText: (text: string) => void;
33
+ controller: MultiSelectController;
34
+ }) => void;
35
+ }): ReactElement;