@mieweb/ui 0.6.1-dev.149 → 0.6.1-dev.150

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 (82) hide show
  1. package/dist/brands/index.cjs +22 -22
  2. package/dist/brands/index.js +5 -5
  3. package/dist/chunk-2T4JU5RH.cjs +1156 -0
  4. package/dist/chunk-2T4JU5RH.cjs.map +1 -0
  5. package/dist/chunk-3SSXDWD7.js +363 -0
  6. package/dist/chunk-3SSXDWD7.js.map +1 -0
  7. package/dist/{chunk-MARLXJQO.cjs → chunk-6R2ZPDN7.cjs} +7 -7
  8. package/dist/{chunk-MARLXJQO.cjs.map → chunk-6R2ZPDN7.cjs.map} +1 -1
  9. package/dist/{chunk-WHUD3XHR.cjs → chunk-7PA26KBF.cjs} +15 -3
  10. package/dist/chunk-7PA26KBF.cjs.map +1 -0
  11. package/dist/chunk-ASLZUFH4.js +1967 -0
  12. package/dist/chunk-ASLZUFH4.js.map +1 -0
  13. package/dist/chunk-FADQVM4M.cjs +2017 -0
  14. package/dist/chunk-FADQVM4M.cjs.map +1 -0
  15. package/dist/{chunk-DFT7TYKL.cjs → chunk-H4T5T65N.cjs} +6 -3
  16. package/dist/chunk-H4T5T65N.cjs.map +1 -0
  17. package/dist/chunk-I6CY5C6A.js +12 -0
  18. package/dist/chunk-I6CY5C6A.js.map +1 -0
  19. package/dist/chunk-JFLC7SHM.cjs +35 -0
  20. package/dist/chunk-JFLC7SHM.cjs.map +1 -0
  21. package/dist/chunk-LZPPH5BW.cjs +368 -0
  22. package/dist/chunk-LZPPH5BW.cjs.map +1 -0
  23. package/dist/{chunk-3OHVUXDG.js → chunk-M7BLVBL4.js} +6 -3
  24. package/dist/chunk-M7BLVBL4.js.map +1 -0
  25. package/dist/chunk-PM2I3QKM.cjs +1419 -0
  26. package/dist/chunk-PM2I3QKM.cjs.map +1 -0
  27. package/dist/{chunk-TW6DXMSD.js → chunk-R6PBBPU3.js} +2 -2
  28. package/dist/{chunk-TW6DXMSD.js.map → chunk-R6PBBPU3.js.map} +1 -1
  29. package/dist/{chunk-33PO3J4O.js → chunk-RXY5SD3O.js} +15 -3
  30. package/dist/chunk-RXY5SD3O.js.map +1 -0
  31. package/dist/{chunk-AEGYWRSL.js → chunk-TXYTMU3K.js} +3 -3
  32. package/dist/{chunk-AEGYWRSL.js.map → chunk-TXYTMU3K.js.map} +1 -1
  33. package/dist/chunk-UHPQYBXQ.js +1124 -0
  34. package/dist/chunk-UHPQYBXQ.js.map +1 -0
  35. package/dist/chunk-XQE26F3G.js +1383 -0
  36. package/dist/chunk-XQE26F3G.js.map +1 -0
  37. package/dist/{chunk-26YNFCOC.cjs → chunk-Z6NRP4Z5.cjs} +2 -2
  38. package/dist/{chunk-26YNFCOC.cjs.map → chunk-Z6NRP4Z5.cjs.map} +1 -1
  39. package/dist/components/Dropdown/index.cjs +7 -7
  40. package/dist/components/Dropdown/index.d.cts +1 -1
  41. package/dist/components/Dropdown/index.d.ts +1 -1
  42. package/dist/components/Dropdown/index.js +1 -1
  43. package/dist/components/RichTextEditor/index.cjs +5 -5
  44. package/dist/components/RichTextEditor/index.js +2 -2
  45. package/dist/components/SuperChat/index.cjs +1319 -0
  46. package/dist/components/SuperChat/index.cjs.map +1 -0
  47. package/dist/components/SuperChat/index.d.cts +189 -0
  48. package/dist/components/SuperChat/index.d.ts +189 -0
  49. package/dist/components/SuperChat/index.js +1282 -0
  50. package/dist/components/SuperChat/index.js.map +1 -0
  51. package/dist/components/SuperChat/plugins/index.cjs +1221 -0
  52. package/dist/components/SuperChat/plugins/index.cjs.map +1 -0
  53. package/dist/components/SuperChat/plugins/index.d.cts +253 -0
  54. package/dist/components/SuperChat/plugins/index.d.ts +253 -0
  55. package/dist/components/SuperChat/plugins/index.js +1181 -0
  56. package/dist/components/SuperChat/plugins/index.js.map +1 -0
  57. package/dist/datavis.cjs +18 -362
  58. package/dist/datavis.cjs.map +1 -1
  59. package/dist/datavis.js +1 -361
  60. package/dist/datavis.js.map +1 -1
  61. package/dist/index.cjs +1297 -5412
  62. package/dist/index.cjs.map +1 -1
  63. package/dist/index.d.cts +44 -240
  64. package/dist/index.d.ts +44 -240
  65. package/dist/index.js +759 -5037
  66. package/dist/index.js.map +1 -1
  67. package/dist/nitroTableGrid-FWRCDE4N.js +22 -0
  68. package/dist/nitroTableGrid-FWRCDE4N.js.map +1 -0
  69. package/dist/nitroTableGrid-IY75TQJ2.cjs +44 -0
  70. package/dist/nitroTableGrid-IY75TQJ2.cjs.map +1 -0
  71. package/dist/styles.css +1 -1
  72. package/dist/tailwind-preset.cjs +4 -4
  73. package/dist/tailwind-preset.js +1 -1
  74. package/dist/types-BFFgW6qy.d.ts +240 -0
  75. package/dist/types-BzeY_kYO.d.cts +242 -0
  76. package/dist/types-BzeY_kYO.d.ts +242 -0
  77. package/dist/types-CRt5IPNL.d.cts +240 -0
  78. package/package.json +41 -4
  79. package/dist/chunk-33PO3J4O.js.map +0 -1
  80. package/dist/chunk-3OHVUXDG.js.map +0 -1
  81. package/dist/chunk-DFT7TYKL.cjs.map +0 -1
  82. package/dist/chunk-WHUD3XHR.cjs.map +0 -1
@@ -0,0 +1,2017 @@
1
+ 'use strict';
2
+
3
+ var chunk2T4JU5RH_cjs = require('./chunk-2T4JU5RH.cjs');
4
+ var chunkOR5DRJCW_cjs = require('./chunk-OR5DRJCW.cjs');
5
+ var React5 = require('react');
6
+ var classVarianceAuthority = require('class-variance-authority');
7
+ var jsxRuntime = require('react/jsx-runtime');
8
+
9
+ function _interopNamespace(e) {
10
+ if (e && e.__esModule) return e;
11
+ var n = Object.create(null);
12
+ if (e) {
13
+ Object.keys(e).forEach(function (k) {
14
+ if (k !== 'default') {
15
+ var d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: function () { return e[k]; }
19
+ });
20
+ }
21
+ });
22
+ }
23
+ n.default = e;
24
+ return Object.freeze(n);
25
+ }
26
+
27
+ var React5__namespace = /*#__PURE__*/_interopNamespace(React5);
28
+
29
+ var statusIconVariants = classVarianceAuthority.cva(
30
+ "inline-flex items-center gap-0.5 text-current",
31
+ {
32
+ variants: {
33
+ status: {
34
+ sending: "text-neutral-500",
35
+ sent: "text-neutral-500",
36
+ delivered: "text-muted-foreground",
37
+ read: "text-primary-800 dark:text-primary-300",
38
+ failed: "text-red-500"
39
+ }
40
+ },
41
+ defaultVariants: {
42
+ status: "sent"
43
+ }
44
+ }
45
+ );
46
+ function MessageStatusIcon({ status, className }) {
47
+ return /* @__PURE__ */ jsxRuntime.jsxs(
48
+ "span",
49
+ {
50
+ className: chunkOR5DRJCW_cjs.cn(statusIconVariants({ status }), className),
51
+ role: "img",
52
+ "aria-label": `Message ${status}`,
53
+ children: [
54
+ status === "sending" && /* @__PURE__ */ jsxRuntime.jsxs(
55
+ "svg",
56
+ {
57
+ "aria-hidden": "true",
58
+ className: "h-3.5 w-3.5 animate-spin",
59
+ fill: "none",
60
+ viewBox: "0 0 24 24",
61
+ children: [
62
+ /* @__PURE__ */ jsxRuntime.jsx(
63
+ "circle",
64
+ {
65
+ className: "opacity-25",
66
+ cx: "12",
67
+ cy: "12",
68
+ r: "10",
69
+ stroke: "currentColor",
70
+ strokeWidth: "4"
71
+ }
72
+ ),
73
+ /* @__PURE__ */ jsxRuntime.jsx(
74
+ "path",
75
+ {
76
+ className: "opacity-75",
77
+ fill: "currentColor",
78
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
79
+ }
80
+ )
81
+ ]
82
+ }
83
+ ),
84
+ status === "sent" && /* @__PURE__ */ jsxRuntime.jsx(
85
+ "svg",
86
+ {
87
+ "aria-hidden": "true",
88
+ className: "h-3.5 w-3.5",
89
+ fill: "none",
90
+ viewBox: "0 0 24 24",
91
+ stroke: "currentColor",
92
+ strokeWidth: "2",
93
+ children: /* @__PURE__ */ jsxRuntime.jsx(
94
+ "path",
95
+ {
96
+ strokeLinecap: "round",
97
+ strokeLinejoin: "round",
98
+ d: "M5 13l4 4L19 7"
99
+ }
100
+ )
101
+ }
102
+ ),
103
+ (status === "delivered" || status === "read") && /* @__PURE__ */ jsxRuntime.jsxs(
104
+ "svg",
105
+ {
106
+ "aria-hidden": "true",
107
+ className: "h-4 w-4",
108
+ fill: "none",
109
+ viewBox: "0 0 24 24",
110
+ stroke: "currentColor",
111
+ strokeWidth: "2",
112
+ children: [
113
+ /* @__PURE__ */ jsxRuntime.jsx(
114
+ "path",
115
+ {
116
+ strokeLinecap: "round",
117
+ strokeLinejoin: "round",
118
+ d: "M5 13l4 4L19 7"
119
+ }
120
+ ),
121
+ /* @__PURE__ */ jsxRuntime.jsx(
122
+ "path",
123
+ {
124
+ strokeLinecap: "round",
125
+ strokeLinejoin: "round",
126
+ d: "M12 13l4 4L26 7",
127
+ transform: "translate(-5, 0)"
128
+ }
129
+ )
130
+ ]
131
+ }
132
+ ),
133
+ status === "failed" && /* @__PURE__ */ jsxRuntime.jsx(
134
+ "svg",
135
+ {
136
+ "aria-hidden": "true",
137
+ className: "h-3.5 w-3.5",
138
+ fill: "none",
139
+ viewBox: "0 0 24 24",
140
+ stroke: "currentColor",
141
+ strokeWidth: "2",
142
+ children: /* @__PURE__ */ jsxRuntime.jsx(
143
+ "path",
144
+ {
145
+ strokeLinecap: "round",
146
+ strokeLinejoin: "round",
147
+ d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
148
+ }
149
+ )
150
+ }
151
+ )
152
+ ]
153
+ }
154
+ );
155
+ }
156
+ function ReadReceiptIndicator({
157
+ receipts,
158
+ maxAvatars = 3,
159
+ size = "xs",
160
+ className
161
+ }) {
162
+ if (receipts.length === 0) return null;
163
+ const visibleReceipts = receipts.slice(0, maxAvatars);
164
+ const remainingCount = receipts.length - maxAvatars;
165
+ const sizeClasses = {
166
+ xs: "h-4 w-4 text-[8px]",
167
+ sm: "h-5 w-5 text-[10px]"
168
+ };
169
+ return /* @__PURE__ */ jsxRuntime.jsxs(
170
+ "div",
171
+ {
172
+ className: chunkOR5DRJCW_cjs.cn("flex items-center -space-x-1", className),
173
+ "aria-label": `Read by ${receipts.map((r) => r.participant.name).join(", ")}`,
174
+ children: [
175
+ visibleReceipts.map((receipt) => /* @__PURE__ */ jsxRuntime.jsx(
176
+ "div",
177
+ {
178
+ className: chunkOR5DRJCW_cjs.cn(
179
+ "rounded-full ring-2 ring-white dark:ring-neutral-900",
180
+ "bg-primary-800 font-medium text-white",
181
+ "flex items-center justify-center",
182
+ sizeClasses[size]
183
+ ),
184
+ title: `Read by ${receipt.participant.name}`,
185
+ children: receipt.participant.avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(
186
+ "img",
187
+ {
188
+ src: receipt.participant.avatarUrl,
189
+ alt: receipt.participant.name,
190
+ className: "h-full w-full rounded-full object-cover"
191
+ }
192
+ ) : receipt.participant.name.charAt(0).toUpperCase()
193
+ },
194
+ receipt.participant.id
195
+ )),
196
+ remainingCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
197
+ "div",
198
+ {
199
+ className: chunkOR5DRJCW_cjs.cn(
200
+ "rounded-full ring-2 ring-white dark:ring-neutral-900",
201
+ "bg-neutral-500 font-medium text-white",
202
+ "flex items-center justify-center",
203
+ sizeClasses[size]
204
+ ),
205
+ children: [
206
+ "+",
207
+ remainingCount
208
+ ]
209
+ }
210
+ )
211
+ ]
212
+ }
213
+ );
214
+ }
215
+ function AttachmentPreview({
216
+ attachment,
217
+ onClick,
218
+ className
219
+ }) {
220
+ const isImage = attachment.type === "image";
221
+ const isVideo = attachment.type === "video";
222
+ if (isImage || isVideo) {
223
+ return /* @__PURE__ */ jsxRuntime.jsxs(
224
+ "button",
225
+ {
226
+ type: "button",
227
+ onClick,
228
+ className: chunkOR5DRJCW_cjs.cn(
229
+ "relative block overflow-hidden rounded-lg",
230
+ "focus:ring-primary-500 focus:ring-2 focus:outline-none",
231
+ "transition-transform hover:scale-[1.02]",
232
+ className
233
+ ),
234
+ "aria-label": `View ${attachment.alt || attachment.filename}`,
235
+ children: [
236
+ /* @__PURE__ */ jsxRuntime.jsx(
237
+ "img",
238
+ {
239
+ src: attachment.thumbnailUrl || attachment.url,
240
+ alt: attachment.alt || attachment.filename,
241
+ className: "max-h-64 w-auto rounded-lg object-cover",
242
+ loading: "lazy"
243
+ }
244
+ ),
245
+ isVideo && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/30", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-full bg-white/90 p-3", children: /* @__PURE__ */ jsxRuntime.jsx(
246
+ "svg",
247
+ {
248
+ "aria-hidden": "true",
249
+ className: "h-6 w-6 text-neutral-900",
250
+ fill: "currentColor",
251
+ viewBox: "0 0 24 24",
252
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 5v14l11-7z" })
253
+ }
254
+ ) }) }),
255
+ attachment.state === "uploading" && attachment.progress !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center text-white", children: [
256
+ /* @__PURE__ */ jsxRuntime.jsxs(
257
+ "svg",
258
+ {
259
+ "aria-hidden": "true",
260
+ className: "mx-auto h-8 w-8 animate-spin",
261
+ fill: "none",
262
+ viewBox: "0 0 24 24",
263
+ children: [
264
+ /* @__PURE__ */ jsxRuntime.jsx(
265
+ "circle",
266
+ {
267
+ className: "opacity-25",
268
+ cx: "12",
269
+ cy: "12",
270
+ r: "10",
271
+ stroke: "currentColor",
272
+ strokeWidth: "4"
273
+ }
274
+ ),
275
+ /* @__PURE__ */ jsxRuntime.jsx(
276
+ "path",
277
+ {
278
+ className: "opacity-75",
279
+ fill: "currentColor",
280
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
281
+ }
282
+ )
283
+ ]
284
+ }
285
+ ),
286
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "mt-1 text-sm", children: [
287
+ attachment.progress,
288
+ "%"
289
+ ] })
290
+ ] }) }),
291
+ attachment.state === "failed" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center text-white", children: [
292
+ /* @__PURE__ */ jsxRuntime.jsx(
293
+ "svg",
294
+ {
295
+ "aria-hidden": "true",
296
+ className: "mx-auto h-8 w-8 text-red-400",
297
+ fill: "none",
298
+ viewBox: "0 0 24 24",
299
+ stroke: "currentColor",
300
+ children: /* @__PURE__ */ jsxRuntime.jsx(
301
+ "path",
302
+ {
303
+ strokeLinecap: "round",
304
+ strokeLinejoin: "round",
305
+ strokeWidth: 2,
306
+ d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
307
+ }
308
+ )
309
+ }
310
+ ),
311
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 text-sm", children: "Upload failed" })
312
+ ] }) })
313
+ ]
314
+ }
315
+ );
316
+ }
317
+ return /* @__PURE__ */ jsxRuntime.jsxs(
318
+ "button",
319
+ {
320
+ type: "button",
321
+ onClick,
322
+ className: chunkOR5DRJCW_cjs.cn(
323
+ "flex items-center gap-3 rounded-lg p-3",
324
+ "bg-white/10 hover:bg-white/20",
325
+ "transition-colors",
326
+ "focus:ring-primary-500 focus:ring-2 focus:outline-none",
327
+ className
328
+ ),
329
+ children: [
330
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg bg-white/20 p-2", children: /* @__PURE__ */ jsxRuntime.jsx(
331
+ "svg",
332
+ {
333
+ "aria-hidden": "true",
334
+ className: "h-6 w-6",
335
+ fill: "none",
336
+ viewBox: "0 0 24 24",
337
+ stroke: "currentColor",
338
+ children: /* @__PURE__ */ jsxRuntime.jsx(
339
+ "path",
340
+ {
341
+ strokeLinecap: "round",
342
+ strokeLinejoin: "round",
343
+ strokeWidth: 2,
344
+ d: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
345
+ }
346
+ )
347
+ }
348
+ ) }),
349
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1 text-left", children: [
350
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-sm font-medium", children: attachment.filename }),
351
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs opacity-70", children: formatFileSize(attachment.size) })
352
+ ] })
353
+ ]
354
+ }
355
+ );
356
+ }
357
+ function formatFileSize(bytes) {
358
+ if (bytes < 1024) return `${bytes} B`;
359
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
360
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
361
+ }
362
+ var bubbleVariants = classVarianceAuthority.cva(
363
+ [
364
+ "relative max-w-[85%] sm:max-w-[70%]",
365
+ "rounded-2xl px-4 py-2",
366
+ "transition-all duration-200"
367
+ ],
368
+ {
369
+ variants: {
370
+ variant: {
371
+ outgoing: ["bg-primary-800 text-white", "rounded-br-md", "ml-auto"],
372
+ incoming: [
373
+ "bg-neutral-100 text-neutral-900",
374
+ "dark:bg-neutral-800 dark:text-neutral-100",
375
+ "rounded-bl-md",
376
+ "mr-auto"
377
+ ],
378
+ system: [
379
+ "mx-auto max-w-none",
380
+ "bg-transparent text-muted-foreground",
381
+ "text-center text-sm",
382
+ "py-1 px-2"
383
+ ]
384
+ },
385
+ status: {
386
+ sending: "",
387
+ sent: "",
388
+ delivered: "",
389
+ read: "",
390
+ failed: "ring-2 ring-red-500/50"
391
+ }
392
+ },
393
+ defaultVariants: {
394
+ variant: "incoming",
395
+ status: "sent"
396
+ }
397
+ }
398
+ );
399
+ function defaultFormatTimestamp(timestamp) {
400
+ const date = typeof timestamp === "string" ? new Date(timestamp) : timestamp;
401
+ return date.toLocaleTimeString(void 0, {
402
+ hour: "numeric",
403
+ minute: "2-digit"
404
+ });
405
+ }
406
+ var MessageBubble = React5__namespace.forwardRef(
407
+ ({
408
+ className,
409
+ message,
410
+ showAvatar = false,
411
+ showSenderName = false,
412
+ showTimestamp = true,
413
+ showStatus = true,
414
+ showReadReceipts = true,
415
+ onRetry,
416
+ onAttachmentClick,
417
+ isOutgoing,
418
+ formatTimestamp = defaultFormatTimestamp,
419
+ ...props
420
+ }, ref) => {
421
+ const isSystem = message.type === "system";
422
+ const variant = isSystem ? "system" : isOutgoing ? "outgoing" : "incoming";
423
+ const hasAttachments = message.attachments && message.attachments.length > 0;
424
+ const hasText = message.content && message.content.trim().length > 0;
425
+ const isFailed = message.status === "failed";
426
+ if (isSystem) {
427
+ return /* @__PURE__ */ jsxRuntime.jsx(
428
+ "div",
429
+ {
430
+ ref,
431
+ "data-slot": "message-system",
432
+ className: chunkOR5DRJCW_cjs.cn(bubbleVariants({ variant: "system" }), className),
433
+ role: "status",
434
+ "aria-live": "polite",
435
+ ...props,
436
+ children: message.content
437
+ }
438
+ );
439
+ }
440
+ return /* @__PURE__ */ jsxRuntime.jsxs(
441
+ "div",
442
+ {
443
+ ref,
444
+ "data-slot": "message-bubble",
445
+ className: chunkOR5DRJCW_cjs.cn(
446
+ "group flex items-end gap-2",
447
+ isOutgoing ? "flex-row-reverse" : "flex-row",
448
+ className
449
+ ),
450
+ ...props,
451
+ children: [
452
+ showAvatar && !isOutgoing && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "message-avatar", className: "mb-1 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
453
+ "div",
454
+ {
455
+ className: chunkOR5DRJCW_cjs.cn(
456
+ "flex h-8 w-8 items-center justify-center rounded-full",
457
+ "bg-primary-800 text-sm font-medium text-white"
458
+ ),
459
+ children: message.sender.avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(
460
+ "img",
461
+ {
462
+ src: message.sender.avatarUrl,
463
+ alt: message.sender.name,
464
+ className: "h-full w-full rounded-full object-cover"
465
+ }
466
+ ) : message.sender.name.charAt(0).toUpperCase()
467
+ }
468
+ ) }),
469
+ /* @__PURE__ */ jsxRuntime.jsxs(
470
+ "div",
471
+ {
472
+ className: chunkOR5DRJCW_cjs.cn(
473
+ "flex min-w-0 flex-1 flex-col",
474
+ isOutgoing ? "items-end" : "items-start"
475
+ ),
476
+ children: [
477
+ showSenderName && !isOutgoing && /* @__PURE__ */ jsxRuntime.jsx(
478
+ "span",
479
+ {
480
+ "data-slot": "message-sender-name",
481
+ className: "text-muted-foreground mb-1 px-1 text-xs font-medium",
482
+ children: message.sender.name
483
+ }
484
+ ),
485
+ message.replyTo && /* @__PURE__ */ jsxRuntime.jsxs(
486
+ "div",
487
+ {
488
+ "data-slot": "message-reply-preview",
489
+ className: chunkOR5DRJCW_cjs.cn(
490
+ "mb-1 max-w-full rounded-lg px-3 py-1.5 text-xs",
491
+ isOutgoing ? "bg-primary-700/50 text-white/80" : "bg-neutral-200 text-neutral-600 dark:bg-neutral-700 dark:text-neutral-300"
492
+ ),
493
+ children: [
494
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: message.replyTo.sender.name }),
495
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate", children: message.replyTo.content })
496
+ ]
497
+ }
498
+ ),
499
+ /* @__PURE__ */ jsxRuntime.jsxs(
500
+ "div",
501
+ {
502
+ "data-slot": "message-bubble-content",
503
+ className: chunkOR5DRJCW_cjs.cn(bubbleVariants({ variant, status: message.status })),
504
+ role: "article",
505
+ "aria-label": `Message from ${message.sender.name}`,
506
+ children: [
507
+ hasAttachments && /* @__PURE__ */ jsxRuntime.jsx("div", { className: chunkOR5DRJCW_cjs.cn("space-y-2", hasText && "mb-2"), children: message.attachments.map((attachment) => /* @__PURE__ */ jsxRuntime.jsx(
508
+ AttachmentPreview,
509
+ {
510
+ attachment,
511
+ onClick: () => onAttachmentClick?.(attachment)
512
+ },
513
+ attachment.id
514
+ )) }),
515
+ hasText && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "break-words whitespace-pre-wrap", children: message.isDeleted ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "italic opacity-60", children: "This message was deleted" }) : message.content }),
516
+ message.isEdited && !message.isDeleted && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 text-xs opacity-60", children: "(edited)" })
517
+ ]
518
+ }
519
+ ),
520
+ /* @__PURE__ */ jsxRuntime.jsxs(
521
+ "div",
522
+ {
523
+ "data-slot": "message-footer",
524
+ className: chunkOR5DRJCW_cjs.cn(
525
+ "mt-1 flex items-center gap-2 px-1",
526
+ isOutgoing ? "flex-row-reverse" : "flex-row"
527
+ ),
528
+ children: [
529
+ showTimestamp && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-xs", children: formatTimestamp(message.timestamp) }),
530
+ showStatus && isOutgoing && /* @__PURE__ */ jsxRuntime.jsx(MessageStatusIcon, { status: message.status }),
531
+ showReadReceipts && isOutgoing && message.readReceipts && message.readReceipts.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(ReadReceiptIndicator, { receipts: message.readReceipts }),
532
+ isFailed && onRetry && /* @__PURE__ */ jsxRuntime.jsxs(
533
+ "button",
534
+ {
535
+ type: "button",
536
+ onClick: onRetry,
537
+ className: chunkOR5DRJCW_cjs.cn(
538
+ "flex items-center gap-1 rounded px-2 py-0.5",
539
+ "text-xs font-medium text-red-700 dark:text-red-400",
540
+ "hover:bg-red-50 dark:hover:bg-red-900/20",
541
+ "focus:ring-2 focus:ring-red-500 focus:outline-none"
542
+ ),
543
+ "aria-label": "Retry sending message",
544
+ children: [
545
+ /* @__PURE__ */ jsxRuntime.jsx(
546
+ "svg",
547
+ {
548
+ "aria-hidden": "true",
549
+ className: "h-3 w-3",
550
+ fill: "none",
551
+ viewBox: "0 0 24 24",
552
+ stroke: "currentColor",
553
+ children: /* @__PURE__ */ jsxRuntime.jsx(
554
+ "path",
555
+ {
556
+ strokeLinecap: "round",
557
+ strokeLinejoin: "round",
558
+ strokeWidth: 2,
559
+ d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
560
+ }
561
+ )
562
+ }
563
+ ),
564
+ "Retry"
565
+ ]
566
+ }
567
+ )
568
+ ]
569
+ }
570
+ ),
571
+ message.reactions && message.reactions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
572
+ "div",
573
+ {
574
+ "data-slot": "message-reactions",
575
+ className: chunkOR5DRJCW_cjs.cn(
576
+ "-mt-1 flex flex-wrap gap-1",
577
+ isOutgoing ? "justify-end" : "justify-start"
578
+ ),
579
+ children: message.reactions.map((reaction) => /* @__PURE__ */ jsxRuntime.jsxs(
580
+ "span",
581
+ {
582
+ className: chunkOR5DRJCW_cjs.cn(
583
+ "inline-flex items-center gap-1 rounded-full px-2 py-0.5",
584
+ "bg-neutral-100 text-xs dark:bg-neutral-800",
585
+ "border border-neutral-200 dark:border-neutral-700"
586
+ ),
587
+ title: reaction.participants.map((p) => p.name).join(", "),
588
+ children: [
589
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: reaction.emoji }),
590
+ reaction.count > 1 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-neutral-500", children: reaction.count })
591
+ ]
592
+ },
593
+ reaction.emoji
594
+ ))
595
+ }
596
+ )
597
+ ]
598
+ }
599
+ ),
600
+ showAvatar && isOutgoing && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 shrink-0" })
601
+ ]
602
+ }
603
+ );
604
+ }
605
+ );
606
+ MessageBubble.displayName = "MessageBubble";
607
+ function groupMessagesByDate(messages) {
608
+ const groups = /* @__PURE__ */ new Map();
609
+ messages.forEach((message) => {
610
+ const date = new Date(message.timestamp);
611
+ const dateKey = date.toDateString();
612
+ const existing = groups.get(dateKey) || [];
613
+ groups.set(dateKey, [...existing, message]);
614
+ });
615
+ return Array.from(groups.entries()).map(([dateKey, msgs]) => ({
616
+ date: dateKey,
617
+ label: formatDateLabel(new Date(dateKey)),
618
+ messages: msgs
619
+ }));
620
+ }
621
+ function formatDateLabel(date) {
622
+ const now = /* @__PURE__ */ new Date();
623
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
624
+ const yesterday = new Date(today);
625
+ yesterday.setDate(yesterday.getDate() - 1);
626
+ const messageDate = new Date(
627
+ date.getFullYear(),
628
+ date.getMonth(),
629
+ date.getDate()
630
+ );
631
+ if (messageDate.getTime() === today.getTime()) {
632
+ return "Today";
633
+ }
634
+ if (messageDate.getTime() === yesterday.getTime()) {
635
+ return "Yesterday";
636
+ }
637
+ if (date.getFullYear() === now.getFullYear()) {
638
+ return date.toLocaleDateString(void 0, {
639
+ weekday: "long",
640
+ month: "long",
641
+ day: "numeric"
642
+ });
643
+ }
644
+ return date.toLocaleDateString(void 0, {
645
+ weekday: "long",
646
+ month: "long",
647
+ day: "numeric",
648
+ year: "numeric"
649
+ });
650
+ }
651
+ function isSameSenderGroup(prev, current, thresholdMinutes = 5) {
652
+ if (!prev) return false;
653
+ if (prev.sender.id !== current.sender.id) return false;
654
+ if (prev.type === "system" || current.type === "system") return false;
655
+ const prevTime = new Date(prev.timestamp).getTime();
656
+ const currentTime = new Date(current.timestamp).getTime();
657
+ const diffMinutes = (currentTime - prevTime) / (1e3 * 60);
658
+ return diffMinutes < thresholdMinutes;
659
+ }
660
+ function SkeletonMessage({
661
+ isOutgoing = false,
662
+ showAvatar = true,
663
+ className
664
+ }) {
665
+ return /* @__PURE__ */ jsxRuntime.jsxs(
666
+ "div",
667
+ {
668
+ "data-slot": "skeleton-message",
669
+ className: chunkOR5DRJCW_cjs.cn(
670
+ "flex items-end gap-2",
671
+ isOutgoing ? "flex-row-reverse" : "flex-row",
672
+ className
673
+ ),
674
+ "aria-hidden": "true",
675
+ children: [
676
+ showAvatar && !isOutgoing && /* @__PURE__ */ jsxRuntime.jsx(
677
+ "div",
678
+ {
679
+ "data-slot": "skeleton-avatar",
680
+ className: "h-8 w-8 animate-pulse rounded-full bg-neutral-200 dark:bg-neutral-700"
681
+ }
682
+ ),
683
+ /* @__PURE__ */ jsxRuntime.jsx(
684
+ "div",
685
+ {
686
+ "data-slot": "skeleton-bubble",
687
+ className: chunkOR5DRJCW_cjs.cn(
688
+ "animate-pulse rounded-2xl",
689
+ isOutgoing ? "bg-primary-800/30 rounded-br-md" : "rounded-bl-md bg-neutral-200 dark:bg-neutral-700",
690
+ "h-10 w-48"
691
+ )
692
+ }
693
+ ),
694
+ showAvatar && isOutgoing && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "skeleton-spacer", className: "w-8" })
695
+ ]
696
+ }
697
+ );
698
+ }
699
+ SkeletonMessage.displayName = "SkeletonMessage";
700
+ function TypingIndicator({ typingState, className }) {
701
+ const { participants } = typingState;
702
+ if (participants.length === 0) return null;
703
+ const typingText = participants.length === 1 ? `${participants[0].name} is typing` : participants.length === 2 ? `${participants[0].name} and ${participants[1].name} are typing` : `${participants[0].name} and ${participants.length - 1} others are typing`;
704
+ return /* @__PURE__ */ jsxRuntime.jsxs(
705
+ "div",
706
+ {
707
+ "data-slot": "typing-indicator",
708
+ className: chunkOR5DRJCW_cjs.cn("flex items-center gap-2 px-4 py-2", className),
709
+ role: "status",
710
+ "aria-live": "polite",
711
+ "aria-label": typingText,
712
+ children: [
713
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 rounded-2xl rounded-bl-md bg-neutral-200 px-4 py-3 dark:bg-neutral-700", children: [
714
+ /* @__PURE__ */ jsxRuntime.jsx(
715
+ "span",
716
+ {
717
+ className: "h-2 w-2 animate-bounce rounded-full bg-neutral-500",
718
+ style: { animationDelay: "0ms" }
719
+ }
720
+ ),
721
+ /* @__PURE__ */ jsxRuntime.jsx(
722
+ "span",
723
+ {
724
+ className: "h-2 w-2 animate-bounce rounded-full bg-neutral-500",
725
+ style: { animationDelay: "150ms" }
726
+ }
727
+ ),
728
+ /* @__PURE__ */ jsxRuntime.jsx(
729
+ "span",
730
+ {
731
+ className: "h-2 w-2 animate-bounce rounded-full bg-neutral-500",
732
+ style: { animationDelay: "300ms" }
733
+ }
734
+ )
735
+ ] }),
736
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-xs", children: typingText })
737
+ ]
738
+ }
739
+ );
740
+ }
741
+ TypingIndicator.displayName = "TypingIndicator";
742
+ function DateSeparator({ label, className }) {
743
+ return /* @__PURE__ */ jsxRuntime.jsx(
744
+ "div",
745
+ {
746
+ "data-slot": "date-separator",
747
+ className: chunkOR5DRJCW_cjs.cn("flex items-center justify-center py-4", className),
748
+ role: "separator",
749
+ "aria-label": label,
750
+ children: /* @__PURE__ */ jsxRuntime.jsx(
751
+ "span",
752
+ {
753
+ className: chunkOR5DRJCW_cjs.cn(
754
+ "rounded-full px-3 py-1 text-xs font-medium",
755
+ "bg-neutral-100 text-neutral-500",
756
+ "dark:bg-neutral-800 dark:text-neutral-400"
757
+ ),
758
+ children: label
759
+ }
760
+ )
761
+ }
762
+ );
763
+ }
764
+ DateSeparator.displayName = "DateSeparator";
765
+ function EmptyState({
766
+ title = "No messages yet",
767
+ description = "Start the conversation by sending a message below.",
768
+ icon,
769
+ action,
770
+ className
771
+ }) {
772
+ return /* @__PURE__ */ jsxRuntime.jsxs(
773
+ "div",
774
+ {
775
+ "data-slot": "message-empty-state",
776
+ className: chunkOR5DRJCW_cjs.cn(
777
+ "flex flex-1 flex-col items-center justify-center p-8 text-center",
778
+ className
779
+ ),
780
+ role: "status",
781
+ "aria-label": title,
782
+ children: [
783
+ icon || /* @__PURE__ */ jsxRuntime.jsx(
784
+ "div",
785
+ {
786
+ "data-slot": "message-empty-state-icon",
787
+ className: "mb-4 rounded-full bg-neutral-100 p-4 dark:bg-neutral-800",
788
+ children: /* @__PURE__ */ jsxRuntime.jsx(
789
+ "svg",
790
+ {
791
+ "aria-hidden": "true",
792
+ className: "h-12 w-12 text-neutral-500",
793
+ fill: "none",
794
+ viewBox: "0 0 24 24",
795
+ stroke: "currentColor",
796
+ children: /* @__PURE__ */ jsxRuntime.jsx(
797
+ "path",
798
+ {
799
+ strokeLinecap: "round",
800
+ strokeLinejoin: "round",
801
+ strokeWidth: 1.5,
802
+ d: "M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
803
+ }
804
+ )
805
+ }
806
+ )
807
+ }
808
+ ),
809
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "mb-2 text-lg font-semibold text-neutral-900 dark:text-neutral-100", children: title }),
810
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground mb-4 max-w-sm text-sm", children: description }),
811
+ action
812
+ ]
813
+ }
814
+ );
815
+ }
816
+ EmptyState.displayName = "EmptyState";
817
+ function LoadMoreButton({
818
+ isLoading,
819
+ onClick,
820
+ className
821
+ }) {
822
+ return /* @__PURE__ */ jsxRuntime.jsx(
823
+ "div",
824
+ {
825
+ "data-slot": "load-more-button",
826
+ className: chunkOR5DRJCW_cjs.cn("flex justify-center py-4", className),
827
+ children: /* @__PURE__ */ jsxRuntime.jsx(
828
+ "button",
829
+ {
830
+ type: "button",
831
+ onClick,
832
+ disabled: isLoading,
833
+ className: chunkOR5DRJCW_cjs.cn(
834
+ "rounded-full px-4 py-2 text-sm font-medium",
835
+ "bg-neutral-100 text-neutral-600 dark:bg-neutral-800 dark:text-neutral-300",
836
+ "hover:bg-neutral-200 dark:hover:bg-neutral-700",
837
+ "focus:ring-primary-500 focus:ring-2 focus:outline-none",
838
+ "disabled:cursor-not-allowed disabled:opacity-50",
839
+ "transition-colors"
840
+ ),
841
+ "aria-label": isLoading ? "Loading more messages" : "Load more messages",
842
+ children: isLoading ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-2", children: [
843
+ /* @__PURE__ */ jsxRuntime.jsxs(
844
+ "svg",
845
+ {
846
+ "aria-hidden": "true",
847
+ className: "h-4 w-4 animate-spin",
848
+ fill: "none",
849
+ viewBox: "0 0 24 24",
850
+ children: [
851
+ /* @__PURE__ */ jsxRuntime.jsx(
852
+ "circle",
853
+ {
854
+ className: "opacity-25",
855
+ cx: "12",
856
+ cy: "12",
857
+ r: "10",
858
+ stroke: "currentColor",
859
+ strokeWidth: "4"
860
+ }
861
+ ),
862
+ /* @__PURE__ */ jsxRuntime.jsx(
863
+ "path",
864
+ {
865
+ className: "opacity-75",
866
+ fill: "currentColor",
867
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
868
+ }
869
+ )
870
+ ]
871
+ }
872
+ ),
873
+ "Loading..."
874
+ ] }) : "Load earlier messages"
875
+ }
876
+ )
877
+ }
878
+ );
879
+ }
880
+ LoadMoreButton.displayName = "LoadMoreButton";
881
+ var MessageList = React5__namespace.forwardRef(
882
+ ({
883
+ messages,
884
+ currentUser,
885
+ isLoading = false,
886
+ hasMore = false,
887
+ isLoadingMore = false,
888
+ typingState,
889
+ showAvatars = true,
890
+ showSenderNames = false,
891
+ groupByDate = true,
892
+ onLoadMore,
893
+ onRetryMessage,
894
+ onAttachmentClick,
895
+ emptyState,
896
+ formatTimestamp,
897
+ className,
898
+ autoScroll = "onNewMessage"
899
+ }, ref) => {
900
+ const scrollContainerRef = React5__namespace.useRef(null);
901
+ const bottomRef = React5__namespace.useRef(null);
902
+ const [isUserScrolled, setIsUserScrolled] = React5__namespace.useState(false);
903
+ const prevMessageCountRef = React5__namespace.useRef(messages.length);
904
+ React5__namespace.useImperativeHandle(ref, () => scrollContainerRef.current);
905
+ const handleScroll = React5__namespace.useCallback(() => {
906
+ const container = scrollContainerRef.current;
907
+ if (!container) return;
908
+ const { scrollTop, scrollHeight, clientHeight } = container;
909
+ const isAtBottom = scrollHeight - scrollTop - clientHeight < 100;
910
+ setIsUserScrolled(!isAtBottom);
911
+ }, []);
912
+ React5__namespace.useEffect(() => {
913
+ const container = scrollContainerRef.current;
914
+ const bottom = bottomRef.current;
915
+ if (!container || !bottom) return;
916
+ const messageCountChanged = messages.length !== prevMessageCountRef.current;
917
+ prevMessageCountRef.current = messages.length;
918
+ if (autoScroll === "always") {
919
+ bottom.scrollIntoView({ behavior: "smooth" });
920
+ } else if (autoScroll === "onNewMessage" && messageCountChanged) {
921
+ const lastMessage = messages[messages.length - 1];
922
+ const isOutgoing = lastMessage?.sender.id === currentUser.id;
923
+ if (isOutgoing || !isUserScrolled) {
924
+ bottom.scrollIntoView({ behavior: "smooth" });
925
+ }
926
+ }
927
+ }, [messages, currentUser.id, autoScroll, isUserScrolled]);
928
+ React5__namespace.useEffect(() => {
929
+ const bottom = bottomRef.current;
930
+ if (bottom && !isLoading) {
931
+ bottom.scrollIntoView();
932
+ }
933
+ }, [isLoading]);
934
+ const messageGroups = groupByDate ? groupMessagesByDate(messages) : [{ date: "all", label: "", messages }];
935
+ if (isLoading) {
936
+ return /* @__PURE__ */ jsxRuntime.jsx(
937
+ "div",
938
+ {
939
+ role: "status",
940
+ className: chunkOR5DRJCW_cjs.cn(
941
+ "flex flex-1 flex-col gap-3 overflow-y-auto p-4",
942
+ className
943
+ ),
944
+ "aria-busy": "true",
945
+ "aria-label": "Loading messages",
946
+ children: Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
947
+ SkeletonMessage,
948
+ {
949
+ isOutgoing: i % 3 === 0,
950
+ showAvatar: showAvatars
951
+ },
952
+ i
953
+ ))
954
+ }
955
+ );
956
+ }
957
+ if (messages.length === 0) {
958
+ return emptyState || /* @__PURE__ */ jsxRuntime.jsx(EmptyState, {});
959
+ }
960
+ return /* @__PURE__ */ jsxRuntime.jsxs(
961
+ "div",
962
+ {
963
+ ref: scrollContainerRef,
964
+ "data-slot": "message-list",
965
+ className: chunkOR5DRJCW_cjs.cn(
966
+ "flex flex-1 flex-col overflow-y-auto",
967
+ "scroll-smooth",
968
+ className
969
+ ),
970
+ onScroll: handleScroll,
971
+ role: "log",
972
+ "aria-label": "Message history",
973
+ "aria-live": "polite",
974
+ children: [
975
+ hasMore && onLoadMore && /* @__PURE__ */ jsxRuntime.jsx(LoadMoreButton, { isLoading: isLoadingMore, onClick: onLoadMore }),
976
+ /* @__PURE__ */ jsxRuntime.jsx(
977
+ "div",
978
+ {
979
+ "data-slot": "message-list-content",
980
+ className: "flex flex-col gap-1 p-4",
981
+ children: messageGroups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs(React5__namespace.Fragment, { children: [
982
+ groupByDate && group.label && /* @__PURE__ */ jsxRuntime.jsx(DateSeparator, { label: group.label }),
983
+ group.messages.map((message, index) => {
984
+ const prevMessage = group.messages[index - 1];
985
+ const isOutgoing = message.sender.id === currentUser.id;
986
+ const isSameGroup = isSameSenderGroup(prevMessage, message);
987
+ return /* @__PURE__ */ jsxRuntime.jsx(
988
+ "div",
989
+ {
990
+ className: chunkOR5DRJCW_cjs.cn(
991
+ "transition-opacity duration-200",
992
+ isSameGroup ? "mt-0.5" : "mt-3",
993
+ index === 0 && "mt-0"
994
+ ),
995
+ children: /* @__PURE__ */ jsxRuntime.jsx(
996
+ MessageBubble,
997
+ {
998
+ message,
999
+ isOutgoing,
1000
+ showAvatar: showAvatars && !isSameGroup && !isOutgoing,
1001
+ showSenderName: showSenderNames && !isSameGroup && !isOutgoing,
1002
+ showTimestamp: !isSameGroup,
1003
+ onRetry: message.status === "failed" && onRetryMessage ? () => onRetryMessage(message.id) : void 0,
1004
+ onAttachmentClick: (attachment) => onAttachmentClick?.(attachment, message),
1005
+ formatTimestamp
1006
+ }
1007
+ )
1008
+ },
1009
+ message.id
1010
+ );
1011
+ })
1012
+ ] }, group.date))
1013
+ }
1014
+ ),
1015
+ typingState && typingState.participants.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(TypingIndicator, { typingState }),
1016
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: bottomRef, className: "h-0", "aria-hidden": "true" }),
1017
+ isUserScrolled && /* @__PURE__ */ jsxRuntime.jsx(
1018
+ "button",
1019
+ {
1020
+ type: "button",
1021
+ onClick: () => {
1022
+ bottomRef.current?.scrollIntoView({ behavior: "smooth" });
1023
+ },
1024
+ className: chunkOR5DRJCW_cjs.cn(
1025
+ "fixed right-4 bottom-24 z-10",
1026
+ "rounded-full p-3 shadow-lg",
1027
+ "bg-white dark:bg-neutral-800",
1028
+ "border border-neutral-200 dark:border-neutral-700",
1029
+ "hover:bg-neutral-50 dark:hover:bg-neutral-700",
1030
+ "focus:ring-primary-500 focus:ring-2 focus:outline-none",
1031
+ "transition-all"
1032
+ ),
1033
+ "aria-label": "Scroll to bottom",
1034
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1035
+ "svg",
1036
+ {
1037
+ "aria-hidden": "true",
1038
+ className: "h-5 w-5 text-neutral-600 dark:text-neutral-300",
1039
+ fill: "none",
1040
+ viewBox: "0 0 24 24",
1041
+ stroke: "currentColor",
1042
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1043
+ "path",
1044
+ {
1045
+ strokeLinecap: "round",
1046
+ strokeLinejoin: "round",
1047
+ strokeWidth: 2,
1048
+ d: "M19 14l-7 7m0 0l-7-7m7 7V3"
1049
+ }
1050
+ )
1051
+ }
1052
+ )
1053
+ }
1054
+ )
1055
+ ]
1056
+ }
1057
+ );
1058
+ }
1059
+ );
1060
+ MessageList.displayName = "MessageList";
1061
+ var headerVariants = classVarianceAuthority.cva(
1062
+ [
1063
+ "flex items-center gap-3 px-4 py-3",
1064
+ "bg-white dark:bg-neutral-900",
1065
+ "border-b border-neutral-200 dark:border-neutral-700"
1066
+ ],
1067
+ {
1068
+ variants: {
1069
+ size: {
1070
+ sm: "py-2",
1071
+ md: "py-3",
1072
+ lg: "py-4"
1073
+ }
1074
+ },
1075
+ defaultVariants: {
1076
+ size: "md"
1077
+ }
1078
+ }
1079
+ );
1080
+ function getConversationTitle(conversation, participant) {
1081
+ if (conversation?.name) return conversation.name;
1082
+ if (participant?.name) return participant.name;
1083
+ if (conversation?.participants && conversation.participants.length > 0) {
1084
+ const names = conversation.participants.filter((p) => !p.isCurrentUser).map((p) => p.name);
1085
+ if (names.length <= 2) return names.join(" & ");
1086
+ return `${names[0]} and ${names.length - 1} others`;
1087
+ }
1088
+ return "Conversation";
1089
+ }
1090
+ function getConversationSubtitle(conversation, participant, showOnlineStatus) {
1091
+ if (participant) {
1092
+ if (showOnlineStatus && participant.isOnline) {
1093
+ return "Online";
1094
+ }
1095
+ if (participant.lastSeen) {
1096
+ const lastSeen = new Date(participant.lastSeen);
1097
+ return `Last seen ${formatLastSeen(lastSeen)}`;
1098
+ }
1099
+ if (participant.phoneNumber) {
1100
+ return participant.phoneNumber;
1101
+ }
1102
+ }
1103
+ if (conversation?.type === "group" && conversation.participants) {
1104
+ return `${conversation.participants.length} participants`;
1105
+ }
1106
+ return void 0;
1107
+ }
1108
+ function formatLastSeen(date) {
1109
+ const now = /* @__PURE__ */ new Date();
1110
+ const diffMs = now.getTime() - date.getTime();
1111
+ const diffMins = Math.floor(diffMs / (1e3 * 60));
1112
+ const diffHours = Math.floor(diffMs / (1e3 * 60 * 60));
1113
+ const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
1114
+ if (diffMins < 1) return "just now";
1115
+ if (diffMins < 60) return `${diffMins}m ago`;
1116
+ if (diffHours < 24) return `${diffHours}h ago`;
1117
+ if (diffDays < 7) return `${diffDays}d ago`;
1118
+ return date.toLocaleDateString();
1119
+ }
1120
+ var ConversationHeader = React5__namespace.forwardRef(
1121
+ ({
1122
+ className,
1123
+ size,
1124
+ conversation,
1125
+ title,
1126
+ subtitle,
1127
+ avatarUrl,
1128
+ participant,
1129
+ showOnlineStatus = true,
1130
+ showBackButton = false,
1131
+ onBack,
1132
+ actions,
1133
+ leftContent,
1134
+ rightContent,
1135
+ ...props
1136
+ }, ref) => {
1137
+ const displayTitle = title || getConversationTitle(conversation, participant);
1138
+ const displaySubtitle = subtitle || getConversationSubtitle(conversation, participant, showOnlineStatus);
1139
+ const displayAvatar = avatarUrl || conversation?.avatarUrl || participant?.avatarUrl;
1140
+ const isOnline = participant?.isOnline;
1141
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1142
+ "header",
1143
+ {
1144
+ ref,
1145
+ "data-slot": "conversation-header",
1146
+ className: chunkOR5DRJCW_cjs.cn(headerVariants({ size }), className),
1147
+ ...props,
1148
+ children: [
1149
+ leftContent || showBackButton && onBack && /* @__PURE__ */ jsxRuntime.jsx(
1150
+ "button",
1151
+ {
1152
+ type: "button",
1153
+ onClick: onBack,
1154
+ className: chunkOR5DRJCW_cjs.cn(
1155
+ "-ml-2 rounded-full p-2",
1156
+ "text-neutral-500 hover:text-neutral-700",
1157
+ "dark:text-neutral-400 dark:hover:text-neutral-200",
1158
+ "hover:bg-neutral-100 dark:hover:bg-neutral-800",
1159
+ "focus:ring-primary-500 focus:ring-2 focus:outline-none",
1160
+ "transition-colors"
1161
+ ),
1162
+ "aria-label": "Go back",
1163
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1164
+ "svg",
1165
+ {
1166
+ "aria-hidden": "true",
1167
+ className: "h-5 w-5",
1168
+ fill: "none",
1169
+ viewBox: "0 0 24 24",
1170
+ stroke: "currentColor",
1171
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1172
+ "path",
1173
+ {
1174
+ strokeLinecap: "round",
1175
+ strokeLinejoin: "round",
1176
+ strokeWidth: 2,
1177
+ d: "M15 19l-7-7 7-7"
1178
+ }
1179
+ )
1180
+ }
1181
+ )
1182
+ }
1183
+ ),
1184
+ /* @__PURE__ */ jsxRuntime.jsxs(
1185
+ "div",
1186
+ {
1187
+ "data-slot": "conversation-header-avatar",
1188
+ className: "relative shrink-0",
1189
+ children: [
1190
+ /* @__PURE__ */ jsxRuntime.jsx(
1191
+ "div",
1192
+ {
1193
+ className: chunkOR5DRJCW_cjs.cn(
1194
+ "flex h-10 w-10 items-center justify-center rounded-full",
1195
+ "bg-primary-800 font-medium text-white"
1196
+ ),
1197
+ children: displayAvatar ? /* @__PURE__ */ jsxRuntime.jsx(
1198
+ "img",
1199
+ {
1200
+ src: displayAvatar,
1201
+ alt: displayTitle,
1202
+ className: "h-full w-full rounded-full object-cover"
1203
+ }
1204
+ ) : displayTitle.charAt(0).toUpperCase()
1205
+ }
1206
+ ),
1207
+ showOnlineStatus && isOnline && /* @__PURE__ */ jsxRuntime.jsx(
1208
+ "span",
1209
+ {
1210
+ className: chunkOR5DRJCW_cjs.cn(
1211
+ "absolute right-0 bottom-0",
1212
+ "h-3 w-3 rounded-full",
1213
+ "bg-green-500 ring-2 ring-white dark:ring-neutral-900"
1214
+ ),
1215
+ role: "status",
1216
+ "aria-label": "Online"
1217
+ }
1218
+ )
1219
+ ]
1220
+ }
1221
+ ),
1222
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-slot": "conversation-header-info", className: "min-w-0 flex-1", children: [
1223
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "truncate font-semibold text-neutral-900 dark:text-neutral-100", children: displayTitle }),
1224
+ displaySubtitle && /* @__PURE__ */ jsxRuntime.jsx(
1225
+ "p",
1226
+ {
1227
+ className: chunkOR5DRJCW_cjs.cn(
1228
+ "truncate text-sm",
1229
+ isOnline ? "text-green-700 dark:text-green-400" : "text-muted-foreground"
1230
+ ),
1231
+ children: displaySubtitle
1232
+ }
1233
+ )
1234
+ ] }),
1235
+ rightContent || actions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex shrink-0 items-center gap-1", children: actions })
1236
+ ]
1237
+ }
1238
+ );
1239
+ }
1240
+ );
1241
+ ConversationHeader.displayName = "ConversationHeader";
1242
+ var ConversationListItem = React5__namespace.forwardRef(({ className, conversation, isSelected, onSelect, ...props }, ref) => {
1243
+ const participant = conversation.participants.find((p) => !p.isCurrentUser);
1244
+ const title = getConversationTitle(conversation, participant);
1245
+ const avatarUrl = conversation.avatarUrl || participant?.avatarUrl;
1246
+ const lastMessage = conversation.lastMessage;
1247
+ const isUnread = conversation.unreadCount > 0;
1248
+ const formatTime = (timestamp) => {
1249
+ const date = new Date(timestamp);
1250
+ const now = /* @__PURE__ */ new Date();
1251
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
1252
+ const messageDate = new Date(
1253
+ date.getFullYear(),
1254
+ date.getMonth(),
1255
+ date.getDate()
1256
+ );
1257
+ if (messageDate.getTime() === today.getTime()) {
1258
+ return date.toLocaleTimeString(void 0, {
1259
+ hour: "numeric",
1260
+ minute: "2-digit"
1261
+ });
1262
+ }
1263
+ const yesterday = new Date(today);
1264
+ yesterday.setDate(yesterday.getDate() - 1);
1265
+ if (messageDate.getTime() === yesterday.getTime()) {
1266
+ return "Yesterday";
1267
+ }
1268
+ return date.toLocaleDateString(void 0, {
1269
+ month: "short",
1270
+ day: "numeric"
1271
+ });
1272
+ };
1273
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1274
+ "button",
1275
+ {
1276
+ ref,
1277
+ type: "button",
1278
+ onClick: () => onSelect?.(conversation),
1279
+ "data-slot": "conversation-list-item",
1280
+ className: chunkOR5DRJCW_cjs.cn(
1281
+ "flex w-full items-center gap-3 px-4 py-3",
1282
+ "text-left transition-colors",
1283
+ isSelected ? "bg-primary-50 dark:bg-primary-900/20" : "hover:bg-neutral-50 dark:hover:bg-neutral-800/50",
1284
+ "focus:bg-neutral-50 focus:outline-none dark:focus:bg-neutral-800/50",
1285
+ className
1286
+ ),
1287
+ "aria-current": isSelected ? "true" : void 0,
1288
+ ...props,
1289
+ children: [
1290
+ /* @__PURE__ */ jsxRuntime.jsxs(
1291
+ "div",
1292
+ {
1293
+ "data-slot": "conversation-list-item-avatar",
1294
+ className: "relative shrink-0",
1295
+ children: [
1296
+ /* @__PURE__ */ jsxRuntime.jsx(
1297
+ "div",
1298
+ {
1299
+ className: chunkOR5DRJCW_cjs.cn(
1300
+ "flex h-12 w-12 items-center justify-center rounded-full",
1301
+ "bg-primary-800 font-medium text-white"
1302
+ ),
1303
+ children: avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(
1304
+ "img",
1305
+ {
1306
+ src: avatarUrl,
1307
+ alt: title,
1308
+ className: "h-full w-full rounded-full object-cover"
1309
+ }
1310
+ ) : title.charAt(0).toUpperCase()
1311
+ }
1312
+ ),
1313
+ participant?.isOnline && /* @__PURE__ */ jsxRuntime.jsx(
1314
+ "span",
1315
+ {
1316
+ className: chunkOR5DRJCW_cjs.cn(
1317
+ "absolute right-0 bottom-0",
1318
+ "h-3 w-3 rounded-full",
1319
+ "bg-green-500 ring-2 ring-white dark:ring-neutral-900"
1320
+ )
1321
+ }
1322
+ )
1323
+ ]
1324
+ }
1325
+ ),
1326
+ /* @__PURE__ */ jsxRuntime.jsxs(
1327
+ "div",
1328
+ {
1329
+ "data-slot": "conversation-list-item-content",
1330
+ className: "min-w-0 flex-1",
1331
+ children: [
1332
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
1333
+ /* @__PURE__ */ jsxRuntime.jsx(
1334
+ "h3",
1335
+ {
1336
+ className: chunkOR5DRJCW_cjs.cn(
1337
+ "truncate text-sm",
1338
+ isUnread ? "font-semibold text-neutral-900 dark:text-neutral-100" : "font-medium text-neutral-700 dark:text-neutral-300"
1339
+ ),
1340
+ children: title
1341
+ }
1342
+ ),
1343
+ lastMessage && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs text-neutral-600 dark:text-neutral-400", children: formatTime(lastMessage.timestamp) })
1344
+ ] }),
1345
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
1346
+ /* @__PURE__ */ jsxRuntime.jsx(
1347
+ "p",
1348
+ {
1349
+ className: chunkOR5DRJCW_cjs.cn(
1350
+ "truncate text-sm",
1351
+ isUnread ? "text-neutral-700 dark:text-neutral-300" : "text-neutral-600 dark:text-neutral-400"
1352
+ ),
1353
+ children: lastMessage?.content || "No messages yet"
1354
+ }
1355
+ ),
1356
+ isUnread && /* @__PURE__ */ jsxRuntime.jsx(
1357
+ "span",
1358
+ {
1359
+ "data-slot": "conversation-list-item-badge",
1360
+ className: chunkOR5DRJCW_cjs.cn(
1361
+ "flex shrink-0 items-center justify-center",
1362
+ "h-5 min-w-[20px] rounded-full px-1.5",
1363
+ "bg-primary-800 text-xs font-medium text-white"
1364
+ ),
1365
+ children: conversation.unreadCount > 99 ? "99+" : conversation.unreadCount
1366
+ }
1367
+ )
1368
+ ] })
1369
+ ]
1370
+ }
1371
+ ),
1372
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 flex-col items-center gap-1", children: [
1373
+ conversation.isPinned && /* @__PURE__ */ jsxRuntime.jsx(
1374
+ "svg",
1375
+ {
1376
+ "aria-hidden": "true",
1377
+ className: "text-primary-800 h-4 w-4",
1378
+ fill: "currentColor",
1379
+ viewBox: "0 0 24 24",
1380
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 4v8l2 2v2h-6v6l-1 1-1-1v-6H4v-2l2-2V4c0-1.1.9-2 2-2h6c1.1 0 2 .9 2 2z" })
1381
+ }
1382
+ ),
1383
+ conversation.isMuted && /* @__PURE__ */ jsxRuntime.jsxs(
1384
+ "svg",
1385
+ {
1386
+ "aria-hidden": "true",
1387
+ className: "h-4 w-4 text-neutral-500",
1388
+ fill: "none",
1389
+ viewBox: "0 0 24 24",
1390
+ stroke: "currentColor",
1391
+ children: [
1392
+ /* @__PURE__ */ jsxRuntime.jsx(
1393
+ "path",
1394
+ {
1395
+ strokeLinecap: "round",
1396
+ strokeLinejoin: "round",
1397
+ strokeWidth: 2,
1398
+ d: "M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z"
1399
+ }
1400
+ ),
1401
+ /* @__PURE__ */ jsxRuntime.jsx(
1402
+ "path",
1403
+ {
1404
+ strokeLinecap: "round",
1405
+ strokeLinejoin: "round",
1406
+ strokeWidth: 2,
1407
+ d: "M17 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2"
1408
+ }
1409
+ )
1410
+ ]
1411
+ }
1412
+ )
1413
+ ] })
1414
+ ]
1415
+ }
1416
+ );
1417
+ });
1418
+ ConversationListItem.displayName = "ConversationListItem";
1419
+ function ConversationListSkeleton({
1420
+ count = 5,
1421
+ className
1422
+ }) {
1423
+ return /* @__PURE__ */ jsxRuntime.jsx(
1424
+ "div",
1425
+ {
1426
+ className: chunkOR5DRJCW_cjs.cn(
1427
+ "divide-y divide-neutral-200 dark:divide-neutral-700",
1428
+ className
1429
+ ),
1430
+ children: Array.from({ length: count }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsxs(
1431
+ "div",
1432
+ {
1433
+ className: "flex items-center gap-3 px-4 py-3",
1434
+ "aria-hidden": "true",
1435
+ children: [
1436
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-12 w-12 animate-pulse rounded-full bg-neutral-200 dark:bg-neutral-700" }),
1437
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-2", children: [
1438
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-32 animate-pulse rounded bg-neutral-200 dark:bg-neutral-700" }),
1439
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 w-48 animate-pulse rounded bg-neutral-200 dark:bg-neutral-700" })
1440
+ ] })
1441
+ ]
1442
+ },
1443
+ i
1444
+ ))
1445
+ }
1446
+ );
1447
+ }
1448
+ ConversationListSkeleton.displayName = "ConversationListSkeleton";
1449
+ function LightboxModal({ attachment, onClose }) {
1450
+ React5__namespace.useEffect(() => {
1451
+ const handleKeyDown = (event) => {
1452
+ if (event.key === "Escape") {
1453
+ onClose();
1454
+ }
1455
+ };
1456
+ if (attachment) {
1457
+ document.addEventListener("keydown", handleKeyDown);
1458
+ document.body.style.overflow = "hidden";
1459
+ }
1460
+ return () => {
1461
+ document.removeEventListener("keydown", handleKeyDown);
1462
+ document.body.style.overflow = "";
1463
+ };
1464
+ }, [attachment, onClose]);
1465
+ if (!attachment) return null;
1466
+ const isImage = attachment.type === "image";
1467
+ const isVideo = attachment.type === "video";
1468
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1469
+ "div",
1470
+ {
1471
+ "data-slot": "message-lightbox",
1472
+ className: chunkOR5DRJCW_cjs.cn("fixed inset-0 z-50", "flex items-center justify-center"),
1473
+ role: "dialog",
1474
+ "aria-modal": "true",
1475
+ "aria-label": `View ${attachment.filename}`,
1476
+ children: [
1477
+ /* @__PURE__ */ jsxRuntime.jsx(
1478
+ "button",
1479
+ {
1480
+ type: "button",
1481
+ className: "absolute inset-0 cursor-default bg-black/90",
1482
+ onClick: onClose,
1483
+ "aria-label": "Close lightbox"
1484
+ }
1485
+ ),
1486
+ /* @__PURE__ */ jsxRuntime.jsx(
1487
+ "button",
1488
+ {
1489
+ type: "button",
1490
+ onClick: onClose,
1491
+ className: chunkOR5DRJCW_cjs.cn(
1492
+ "absolute top-4 right-4 z-10",
1493
+ "rounded-full p-2",
1494
+ "bg-white/10 text-white",
1495
+ "hover:bg-white/20",
1496
+ "focus:ring-2 focus:ring-white focus:outline-none",
1497
+ "transition-colors"
1498
+ ),
1499
+ "aria-label": "Close",
1500
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1501
+ "svg",
1502
+ {
1503
+ "aria-hidden": "true",
1504
+ className: "h-6 w-6",
1505
+ fill: "none",
1506
+ viewBox: "0 0 24 24",
1507
+ stroke: "currentColor",
1508
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1509
+ "path",
1510
+ {
1511
+ strokeLinecap: "round",
1512
+ strokeLinejoin: "round",
1513
+ strokeWidth: 2,
1514
+ d: "M6 18L18 6M6 6l12 12"
1515
+ }
1516
+ )
1517
+ }
1518
+ )
1519
+ }
1520
+ ),
1521
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative z-10 max-h-[90vh] max-w-[90vw]", children: [
1522
+ isImage && /* @__PURE__ */ jsxRuntime.jsx(
1523
+ "img",
1524
+ {
1525
+ src: attachment.url,
1526
+ alt: attachment.alt || attachment.filename,
1527
+ className: "max-h-[90vh] max-w-[90vw] object-contain"
1528
+ }
1529
+ ),
1530
+ isVideo && /* @__PURE__ */ jsxRuntime.jsx(
1531
+ "video",
1532
+ {
1533
+ src: attachment.url,
1534
+ controls: true,
1535
+ autoPlay: true,
1536
+ className: "max-h-[90vh] max-w-[90vw]",
1537
+ children: /* @__PURE__ */ jsxRuntime.jsx("track", { kind: "captions" })
1538
+ }
1539
+ )
1540
+ ] }),
1541
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-4 left-1/2 -translate-x-1/2", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "rounded-full bg-black/50 px-4 py-2 text-sm text-white", children: attachment.filename }) })
1542
+ ]
1543
+ }
1544
+ );
1545
+ }
1546
+ LightboxModal.displayName = "LightboxModal";
1547
+ var MessageThread = React5__namespace.forwardRef(
1548
+ ({
1549
+ conversation,
1550
+ messages,
1551
+ currentUser,
1552
+ typingState,
1553
+ isLoading = false,
1554
+ hasMore = false,
1555
+ isLoadingMore = false,
1556
+ isSending = false,
1557
+ eventHandlers = {},
1558
+ showHeader = true,
1559
+ showBackButton = false,
1560
+ onBack,
1561
+ headerActions,
1562
+ placeholder = "Type a message...",
1563
+ maxMessageLength = 1600,
1564
+ showCharacterCount = false,
1565
+ showAttachmentPicker = true,
1566
+ showCameraButton = false,
1567
+ acceptedFileTypes,
1568
+ maxFileSize,
1569
+ maxAttachments,
1570
+ showAvatars = true,
1571
+ showSenderNames = false,
1572
+ groupByDate = true,
1573
+ emptyState,
1574
+ formatTimestamp,
1575
+ onError,
1576
+ className
1577
+ }, ref) => {
1578
+ const [lightboxAttachment, setLightboxAttachment] = React5__namespace.useState(null);
1579
+ const [replyTo, setReplyTo] = React5__namespace.useState(null);
1580
+ const participant = conversation?.type === "direct" ? conversation.participants.find((p) => p.id !== currentUser.id) : void 0;
1581
+ const handleAttachmentClick = (attachment, message) => {
1582
+ if (attachment.type === "image" || attachment.type === "video") {
1583
+ setLightboxAttachment(attachment);
1584
+ }
1585
+ eventHandlers.onAttachmentClick?.(attachment, message);
1586
+ };
1587
+ const handleSendMessage = async (newMessage) => {
1588
+ const messageWithReply = {
1589
+ ...newMessage,
1590
+ replyToId: replyTo?.id || newMessage.replyToId
1591
+ };
1592
+ setReplyTo(null);
1593
+ await eventHandlers.onSendMessage?.(messageWithReply);
1594
+ };
1595
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1596
+ "div",
1597
+ {
1598
+ ref,
1599
+ "data-slot": "message-thread",
1600
+ className: chunkOR5DRJCW_cjs.cn(
1601
+ "flex h-full flex-col",
1602
+ "bg-white dark:bg-neutral-900",
1603
+ className
1604
+ ),
1605
+ children: [
1606
+ showHeader && /* @__PURE__ */ jsxRuntime.jsx(
1607
+ ConversationHeader,
1608
+ {
1609
+ conversation,
1610
+ participant,
1611
+ showBackButton,
1612
+ onBack,
1613
+ actions: headerActions
1614
+ }
1615
+ ),
1616
+ /* @__PURE__ */ jsxRuntime.jsx(
1617
+ chunk2T4JU5RH_cjs.DragDropZone,
1618
+ {
1619
+ onFilesDropped: () => {
1620
+ onError?.("Drop files on the composer to attach them");
1621
+ },
1622
+ disabled: !showAttachmentPicker,
1623
+ className: "flex-1 overflow-hidden",
1624
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1625
+ MessageList,
1626
+ {
1627
+ messages,
1628
+ currentUser,
1629
+ isLoading,
1630
+ hasMore,
1631
+ isLoadingMore,
1632
+ typingState,
1633
+ showAvatars,
1634
+ showSenderNames,
1635
+ groupByDate,
1636
+ onLoadMore: eventHandlers.onLoadMore,
1637
+ onRetryMessage: eventHandlers.onRetryMessage,
1638
+ onAttachmentClick: handleAttachmentClick,
1639
+ emptyState,
1640
+ formatTimestamp,
1641
+ className: "h-full"
1642
+ }
1643
+ )
1644
+ }
1645
+ ),
1646
+ /* @__PURE__ */ jsxRuntime.jsx(
1647
+ chunk2T4JU5RH_cjs.MessageComposer,
1648
+ {
1649
+ onSend: handleSendMessage,
1650
+ onTypingStart: eventHandlers.onTypingStart,
1651
+ onTypingStop: eventHandlers.onTypingStop,
1652
+ placeholder,
1653
+ maxLength: maxMessageLength,
1654
+ showCharacterCount,
1655
+ isSending,
1656
+ showAttachmentPicker,
1657
+ showCameraButton,
1658
+ acceptedFileTypes,
1659
+ maxFileSize,
1660
+ maxAttachments,
1661
+ onError,
1662
+ replyTo,
1663
+ onCancelReply: () => setReplyTo(null)
1664
+ }
1665
+ ),
1666
+ /* @__PURE__ */ jsxRuntime.jsx(
1667
+ LightboxModal,
1668
+ {
1669
+ attachment: lightboxAttachment,
1670
+ onClose: () => setLightboxAttachment(null)
1671
+ }
1672
+ )
1673
+ ]
1674
+ }
1675
+ );
1676
+ }
1677
+ );
1678
+ MessageThread.displayName = "MessageThread";
1679
+ function MessagingSplitView({
1680
+ conversationList,
1681
+ messageThread,
1682
+ hasSelectedConversation = false,
1683
+ listWidth = 320,
1684
+ mobileBreakpoint = "md",
1685
+ className
1686
+ }) {
1687
+ const breakpointClasses = {
1688
+ sm: "sm:flex",
1689
+ md: "md:flex",
1690
+ lg: "lg:flex"
1691
+ };
1692
+ const hideMobileClasses = {
1693
+ sm: hasSelectedConversation ? "hidden sm:block" : "block sm:block",
1694
+ md: hasSelectedConversation ? "hidden md:block" : "block md:block",
1695
+ lg: hasSelectedConversation ? "hidden lg:block" : "block lg:block"
1696
+ };
1697
+ const showMobileClasses = {
1698
+ sm: hasSelectedConversation ? "block sm:block" : "hidden sm:block",
1699
+ md: hasSelectedConversation ? "block md:block" : "hidden md:block",
1700
+ lg: hasSelectedConversation ? "block lg:block" : "hidden lg:block"
1701
+ };
1702
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1703
+ "div",
1704
+ {
1705
+ "data-slot": "messaging-split-view",
1706
+ className: chunkOR5DRJCW_cjs.cn(
1707
+ "h-full w-full",
1708
+ breakpointClasses[mobileBreakpoint],
1709
+ className
1710
+ ),
1711
+ children: [
1712
+ /* @__PURE__ */ jsxRuntime.jsx(
1713
+ "div",
1714
+ {
1715
+ "data-slot": "messaging-list-pane",
1716
+ className: chunkOR5DRJCW_cjs.cn(
1717
+ "h-full w-full flex-shrink-0",
1718
+ "border-r border-neutral-200 dark:border-neutral-700",
1719
+ hideMobileClasses[mobileBreakpoint]
1720
+ ),
1721
+ style: {
1722
+ width: typeof listWidth === "number" ? `${listWidth}px` : listWidth
1723
+ },
1724
+ children: conversationList
1725
+ }
1726
+ ),
1727
+ /* @__PURE__ */ jsxRuntime.jsx(
1728
+ "div",
1729
+ {
1730
+ "data-slot": "messaging-thread-pane",
1731
+ className: chunkOR5DRJCW_cjs.cn(
1732
+ "h-full min-w-0 flex-1",
1733
+ showMobileClasses[mobileBreakpoint]
1734
+ ),
1735
+ children: messageThread
1736
+ }
1737
+ )
1738
+ ]
1739
+ }
1740
+ );
1741
+ }
1742
+ MessagingSplitView.displayName = "MessagingSplitView";
1743
+ function useMessages(options) {
1744
+ const {
1745
+ initialMessages = [],
1746
+ currentUser,
1747
+ onSend,
1748
+ onRetry,
1749
+ onLoadMore
1750
+ } = options;
1751
+ const [messages, setMessages] = React5__namespace.useState(initialMessages);
1752
+ const [isSending, setIsSending] = React5__namespace.useState(false);
1753
+ const [isLoadingMore, setIsLoadingMore] = React5__namespace.useState(false);
1754
+ React5__namespace.useEffect(() => {
1755
+ setMessages(initialMessages);
1756
+ }, [initialMessages]);
1757
+ const addMessage = React5__namespace.useCallback((message) => {
1758
+ setMessages((prev) => {
1759
+ if (prev.some((m) => m.id === message.id)) {
1760
+ return prev;
1761
+ }
1762
+ return [...prev, message];
1763
+ });
1764
+ }, []);
1765
+ const updateMessage = React5__namespace.useCallback(
1766
+ (messageId, updates) => {
1767
+ setMessages(
1768
+ (prev) => prev.map((m) => m.id === messageId ? { ...m, ...updates } : m)
1769
+ );
1770
+ },
1771
+ []
1772
+ );
1773
+ const removeMessage = React5__namespace.useCallback((messageId) => {
1774
+ setMessages((prev) => prev.filter((m) => m.id !== messageId));
1775
+ }, []);
1776
+ const updateStatus = React5__namespace.useCallback(
1777
+ (messageId, status) => {
1778
+ updateMessage(messageId, { status });
1779
+ },
1780
+ [updateMessage]
1781
+ );
1782
+ const markAsRead = React5__namespace.useCallback(
1783
+ (messageId) => {
1784
+ updateStatus(messageId, "read");
1785
+ },
1786
+ [updateStatus]
1787
+ );
1788
+ const sendMessage = React5__namespace.useCallback(
1789
+ async (newMessage) => {
1790
+ const optimisticId = `optimistic-${Date.now()}`;
1791
+ const optimisticMessage = {
1792
+ id: optimisticId,
1793
+ type: "text",
1794
+ content: newMessage.content,
1795
+ sender: currentUser,
1796
+ timestamp: /* @__PURE__ */ new Date(),
1797
+ status: "sending",
1798
+ attachments: []
1799
+ // Would handle attachment uploads here
1800
+ };
1801
+ addMessage(optimisticMessage);
1802
+ setIsSending(true);
1803
+ try {
1804
+ if (onSend) {
1805
+ const sentMessage = await onSend(newMessage);
1806
+ setMessages(
1807
+ (prev) => prev.map((m) => m.id === optimisticId ? sentMessage : m)
1808
+ );
1809
+ } else {
1810
+ updateStatus(optimisticId, "sent");
1811
+ }
1812
+ } catch {
1813
+ updateStatus(optimisticId, "failed");
1814
+ } finally {
1815
+ setIsSending(false);
1816
+ }
1817
+ },
1818
+ [currentUser, onSend, addMessage, updateStatus]
1819
+ );
1820
+ const retryMessage = React5__namespace.useCallback(
1821
+ async (messageId) => {
1822
+ updateStatus(messageId, "sending");
1823
+ try {
1824
+ if (onRetry) {
1825
+ await onRetry(messageId);
1826
+ updateStatus(messageId, "sent");
1827
+ }
1828
+ } catch {
1829
+ updateStatus(messageId, "failed");
1830
+ }
1831
+ },
1832
+ [onRetry, updateStatus]
1833
+ );
1834
+ const loadMore = React5__namespace.useCallback(async () => {
1835
+ if (isLoadingMore || !onLoadMore) return;
1836
+ setIsLoadingMore(true);
1837
+ try {
1838
+ const olderMessages = await onLoadMore();
1839
+ setMessages((prev) => [...olderMessages, ...prev]);
1840
+ } finally {
1841
+ setIsLoadingMore(false);
1842
+ }
1843
+ }, [isLoadingMore, onLoadMore]);
1844
+ return {
1845
+ messages,
1846
+ addMessage,
1847
+ updateMessage,
1848
+ removeMessage,
1849
+ sendMessage,
1850
+ retryMessage,
1851
+ loadMore,
1852
+ isSending,
1853
+ isLoadingMore,
1854
+ markAsRead,
1855
+ updateStatus
1856
+ };
1857
+ }
1858
+ function useTypingIndicator(options = {}) {
1859
+ const {
1860
+ typingParticipants: initialParticipants = [],
1861
+ debounceTime = 2e3,
1862
+ onTypingStart,
1863
+ onTypingStop
1864
+ } = options;
1865
+ const [participants, setParticipants] = React5__namespace.useState(initialParticipants);
1866
+ const [isLocalTyping, setIsLocalTyping] = React5__namespace.useState(false);
1867
+ const typingTimeoutRef = React5__namespace.useRef(null);
1868
+ React5__namespace.useEffect(() => {
1869
+ setParticipants(initialParticipants);
1870
+ }, [initialParticipants]);
1871
+ const startTyping = React5__namespace.useCallback(() => {
1872
+ if (!isLocalTyping) {
1873
+ setIsLocalTyping(true);
1874
+ onTypingStart?.();
1875
+ }
1876
+ if (typingTimeoutRef.current) {
1877
+ clearTimeout(typingTimeoutRef.current);
1878
+ }
1879
+ typingTimeoutRef.current = setTimeout(() => {
1880
+ setIsLocalTyping(false);
1881
+ onTypingStop?.();
1882
+ }, debounceTime);
1883
+ }, [isLocalTyping, debounceTime, onTypingStart, onTypingStop]);
1884
+ const stopTyping = React5__namespace.useCallback(() => {
1885
+ if (typingTimeoutRef.current) {
1886
+ clearTimeout(typingTimeoutRef.current);
1887
+ }
1888
+ setIsLocalTyping(false);
1889
+ onTypingStop?.();
1890
+ }, [onTypingStop]);
1891
+ React5__namespace.useEffect(() => {
1892
+ return () => {
1893
+ if (typingTimeoutRef.current) {
1894
+ clearTimeout(typingTimeoutRef.current);
1895
+ }
1896
+ };
1897
+ }, []);
1898
+ const typingState = React5__namespace.useMemo(
1899
+ () => ({
1900
+ participants,
1901
+ lastUpdated: /* @__PURE__ */ new Date()
1902
+ }),
1903
+ [participants]
1904
+ );
1905
+ return {
1906
+ typingState,
1907
+ startTyping,
1908
+ stopTyping,
1909
+ setTypingParticipants: setParticipants
1910
+ };
1911
+ }
1912
+ function useMessageScroll(options) {
1913
+ const { messages, currentUserId, threshold = 100 } = options;
1914
+ const scrollContainerRef = React5__namespace.useRef(null);
1915
+ const bottomRef = React5__namespace.useRef(null);
1916
+ const [isScrolledUp, setIsScrolledUp] = React5__namespace.useState(false);
1917
+ const prevMessageCountRef = React5__namespace.useRef(messages.length);
1918
+ React5__namespace.useEffect(() => {
1919
+ const container = scrollContainerRef.current;
1920
+ if (!container) return;
1921
+ const handleScroll = () => {
1922
+ const { scrollTop, scrollHeight, clientHeight } = container;
1923
+ const isAtBottom = scrollHeight - scrollTop - clientHeight < threshold;
1924
+ setIsScrolledUp(!isAtBottom);
1925
+ };
1926
+ container.addEventListener("scroll", handleScroll);
1927
+ return () => container.removeEventListener("scroll", handleScroll);
1928
+ }, [threshold]);
1929
+ const scrollToBottom = React5__namespace.useCallback((smooth = true) => {
1930
+ bottomRef.current?.scrollIntoView({
1931
+ behavior: smooth ? "smooth" : "auto"
1932
+ });
1933
+ }, []);
1934
+ React5__namespace.useEffect(() => {
1935
+ const messageCountChanged = messages.length !== prevMessageCountRef.current;
1936
+ prevMessageCountRef.current = messages.length;
1937
+ if (!messageCountChanged) return;
1938
+ const lastMessage = messages[messages.length - 1];
1939
+ const isOutgoing = lastMessage?.sender.id === currentUserId;
1940
+ if (isOutgoing || !isScrolledUp) {
1941
+ scrollToBottom(true);
1942
+ }
1943
+ }, [messages, currentUserId, isScrolledUp, scrollToBottom]);
1944
+ return {
1945
+ scrollContainerRef,
1946
+ bottomRef,
1947
+ isScrolledUp,
1948
+ scrollToBottom
1949
+ };
1950
+ }
1951
+ function useReadReceipts(options) {
1952
+ const { currentUserId, onMarkRead, threshold = 0.5 } = options;
1953
+ const observerRef = React5__namespace.useRef(null);
1954
+ const observedMessagesRef = React5__namespace.useRef(/* @__PURE__ */ new Set());
1955
+ React5__namespace.useEffect(() => {
1956
+ observerRef.current = new IntersectionObserver(
1957
+ (entries) => {
1958
+ entries.forEach((entry) => {
1959
+ if (entry.isIntersecting) {
1960
+ const messageId = entry.target.getAttribute("data-message-id");
1961
+ if (messageId && !observedMessagesRef.current.has(messageId)) {
1962
+ observedMessagesRef.current.add(messageId);
1963
+ onMarkRead?.(messageId);
1964
+ }
1965
+ }
1966
+ });
1967
+ },
1968
+ { threshold }
1969
+ );
1970
+ return () => {
1971
+ observerRef.current?.disconnect();
1972
+ };
1973
+ }, [onMarkRead, threshold]);
1974
+ const observeMessage = React5__namespace.useCallback(
1975
+ (element, message) => {
1976
+ if (!element || !observerRef.current) return;
1977
+ if (message.sender.id !== currentUserId && message.status !== "read" && !observedMessagesRef.current.has(message.id)) {
1978
+ element.setAttribute("data-message-id", message.id);
1979
+ observerRef.current.observe(element);
1980
+ }
1981
+ },
1982
+ [currentUserId]
1983
+ );
1984
+ return { observeMessage };
1985
+ }
1986
+
1987
+ exports.AttachmentPreview = AttachmentPreview;
1988
+ exports.ConversationHeader = ConversationHeader;
1989
+ exports.ConversationListItem = ConversationListItem;
1990
+ exports.ConversationListSkeleton = ConversationListSkeleton;
1991
+ exports.DateSeparator = DateSeparator;
1992
+ exports.EmptyState = EmptyState;
1993
+ exports.LightboxModal = LightboxModal;
1994
+ exports.LoadMoreButton = LoadMoreButton;
1995
+ exports.MessageBubble = MessageBubble;
1996
+ exports.MessageList = MessageList;
1997
+ exports.MessageStatusIcon = MessageStatusIcon;
1998
+ exports.MessageThread = MessageThread;
1999
+ exports.MessagingSplitView = MessagingSplitView;
2000
+ exports.ReadReceiptIndicator = ReadReceiptIndicator;
2001
+ exports.SkeletonMessage = SkeletonMessage;
2002
+ exports.TypingIndicator = TypingIndicator;
2003
+ exports.bubbleVariants = bubbleVariants;
2004
+ exports.formatDateLabel = formatDateLabel;
2005
+ exports.formatFileSize = formatFileSize;
2006
+ exports.formatLastSeen = formatLastSeen;
2007
+ exports.getConversationSubtitle = getConversationSubtitle;
2008
+ exports.getConversationTitle = getConversationTitle;
2009
+ exports.groupMessagesByDate = groupMessagesByDate;
2010
+ exports.headerVariants = headerVariants;
2011
+ exports.isSameSenderGroup = isSameSenderGroup;
2012
+ exports.useMessageScroll = useMessageScroll;
2013
+ exports.useMessages = useMessages;
2014
+ exports.useReadReceipts = useReadReceipts;
2015
+ exports.useTypingIndicator = useTypingIndicator;
2016
+ //# sourceMappingURL=chunk-FADQVM4M.cjs.map
2017
+ //# sourceMappingURL=chunk-FADQVM4M.cjs.map