@mieweb/ui 0.9.0-dev.0 → 0.9.0-dev.1

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 (31) hide show
  1. package/dist/{chunk-WIAQQNFU.cjs → chunk-2A2FLQJC.cjs} +1138 -12
  2. package/dist/chunk-2A2FLQJC.cjs.map +1 -0
  3. package/dist/{chunk-5BVFVXHN.cjs → chunk-5VXRKOPR.cjs} +16 -525
  4. package/dist/chunk-5VXRKOPR.cjs.map +1 -0
  5. package/dist/{chunk-O6GJ4IYS.js → chunk-IF3X2WZP.js} +16 -521
  6. package/dist/chunk-IF3X2WZP.js.map +1 -0
  7. package/dist/{chunk-22CYDCXA.js → chunk-KATEJG2F.js} +1137 -13
  8. package/dist/chunk-KATEJG2F.js.map +1 -0
  9. package/dist/{chunk-FQBTGS3E.cjs → chunk-O425FKDI.cjs} +563 -55
  10. package/dist/chunk-O425FKDI.cjs.map +1 -0
  11. package/dist/{chunk-MXQ7J4BB.js → chunk-RPG2ZP2T.js} +557 -53
  12. package/dist/chunk-RPG2ZP2T.js.map +1 -0
  13. package/dist/components/SuperChat/index.cjs +36 -17
  14. package/dist/components/SuperChat/index.cjs.map +1 -1
  15. package/dist/components/SuperChat/index.d.cts +16 -3
  16. package/dist/components/SuperChat/index.d.ts +16 -3
  17. package/dist/components/SuperChat/index.js +31 -12
  18. package/dist/components/SuperChat/index.js.map +1 -1
  19. package/dist/components/SuperChat/plugins/index.cjs +3 -3
  20. package/dist/components/SuperChat/plugins/index.js +2 -2
  21. package/dist/index.cjs +820 -1930
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.js +663 -1780
  24. package/dist/index.js.map +1 -1
  25. package/package.json +1 -1
  26. package/dist/chunk-22CYDCXA.js.map +0 -1
  27. package/dist/chunk-5BVFVXHN.cjs.map +0 -1
  28. package/dist/chunk-FQBTGS3E.cjs.map +0 -1
  29. package/dist/chunk-MXQ7J4BB.js.map +0 -1
  30. package/dist/chunk-O6GJ4IYS.js.map +0 -1
  31. package/dist/chunk-WIAQQNFU.cjs.map +0 -1
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var chunk5BVFVXHN_cjs = require('./chunk-5BVFVXHN.cjs');
3
+ var chunk5VXRKOPR_cjs = require('./chunk-5VXRKOPR.cjs');
4
4
  var chunkS5RVSXDV_cjs = require('./chunk-S5RVSXDV.cjs');
5
- var React5 = require('react');
5
+ var React6 = require('react');
6
6
  var classVarianceAuthority = require('class-variance-authority');
7
7
  var jsxRuntime = require('react/jsx-runtime');
8
8
 
@@ -24,8 +24,512 @@ function _interopNamespace(e) {
24
24
  return Object.freeze(n);
25
25
  }
26
26
 
27
- var React5__namespace = /*#__PURE__*/_interopNamespace(React5);
27
+ var React6__namespace = /*#__PURE__*/_interopNamespace(React6);
28
28
 
29
+ function isFileAccepted(file, accepted) {
30
+ if (!accepted || accepted.length === 0) return true;
31
+ const mime = file.type;
32
+ const name = file.name.toLowerCase();
33
+ return accepted.some((token) => {
34
+ if (token.startsWith(".")) return name.endsWith(token.toLowerCase());
35
+ if (token.endsWith("/*")) return mime.startsWith(token.slice(0, -1));
36
+ if (token.includes("/")) return mime === token;
37
+ return false;
38
+ });
39
+ }
40
+ function CharacterCounter({
41
+ current,
42
+ max,
43
+ showWarningAt = 0.9,
44
+ className
45
+ }) {
46
+ const percentage = current / max;
47
+ const isWarning = percentage >= showWarningAt && percentage < 1;
48
+ const isOver = current > max;
49
+ return /* @__PURE__ */ jsxRuntime.jsxs(
50
+ "span",
51
+ {
52
+ className: chunkS5RVSXDV_cjs.cn(
53
+ "text-xs tabular-nums",
54
+ isOver ? "font-medium text-red-700 dark:text-red-400" : isWarning ? "text-amber-700 dark:text-amber-400" : "text-neutral-600 dark:text-neutral-400",
55
+ className
56
+ ),
57
+ "aria-live": "polite",
58
+ "aria-label": `${current} of ${max} characters`,
59
+ children: [
60
+ current,
61
+ "/",
62
+ max
63
+ ]
64
+ }
65
+ );
66
+ }
67
+ CharacterCounter.displayName = "CharacterCounter";
68
+ var sendButtonVariants = classVarianceAuthority.cva(
69
+ [
70
+ "inline-flex items-center justify-center",
71
+ "rounded-full p-3 self-start",
72
+ "transition-all duration-200",
73
+ "focus:outline-none focus:ring-2 focus:ring-primary-500",
74
+ "disabled:opacity-50 disabled:cursor-not-allowed"
75
+ ],
76
+ {
77
+ variants: {
78
+ variant: {
79
+ primary: [
80
+ "bg-primary-800 text-white",
81
+ "hover:bg-primary-900",
82
+ "active:scale-95"
83
+ ],
84
+ subtle: [
85
+ "bg-transparent text-primary-800",
86
+ "hover:bg-primary-50 dark:hover:bg-primary-900/20"
87
+ ]
88
+ },
89
+ canSend: {
90
+ true: "",
91
+ false: "opacity-50 cursor-not-allowed"
92
+ }
93
+ },
94
+ defaultVariants: {
95
+ variant: "primary",
96
+ canSend: false
97
+ }
98
+ }
99
+ );
100
+ var SendButton = React6__namespace.forwardRef(
101
+ ({ className, variant, canSend, isLoading, disabled, ...props }, ref) => {
102
+ return /* @__PURE__ */ jsxRuntime.jsx(
103
+ "button",
104
+ {
105
+ ref,
106
+ type: "submit",
107
+ disabled: disabled || !canSend || isLoading,
108
+ "data-slot": "composer-send-button",
109
+ className: chunkS5RVSXDV_cjs.cn(sendButtonVariants({ variant, canSend }), className),
110
+ "aria-label": isLoading ? "Sending message" : "Send message",
111
+ ...props,
112
+ children: isLoading ? /* @__PURE__ */ jsxRuntime.jsxs(
113
+ "svg",
114
+ {
115
+ "aria-hidden": "true",
116
+ className: "h-5 w-5 animate-spin",
117
+ fill: "none",
118
+ viewBox: "0 0 24 24",
119
+ children: [
120
+ /* @__PURE__ */ jsxRuntime.jsx(
121
+ "circle",
122
+ {
123
+ className: "opacity-25",
124
+ cx: "12",
125
+ cy: "12",
126
+ r: "10",
127
+ stroke: "currentColor",
128
+ strokeWidth: "4"
129
+ }
130
+ ),
131
+ /* @__PURE__ */ jsxRuntime.jsx(
132
+ "path",
133
+ {
134
+ className: "opacity-75",
135
+ fill: "currentColor",
136
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
137
+ }
138
+ )
139
+ ]
140
+ }
141
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
142
+ "svg",
143
+ {
144
+ "aria-hidden": "true",
145
+ className: "h-5 w-5",
146
+ fill: "none",
147
+ viewBox: "0 0 24 24",
148
+ stroke: "currentColor",
149
+ children: /* @__PURE__ */ jsxRuntime.jsx(
150
+ "path",
151
+ {
152
+ strokeLinecap: "round",
153
+ strokeLinejoin: "round",
154
+ strokeWidth: 2,
155
+ d: "M12 19l9 2-9-18-9 18 9-2zm0 0v-8"
156
+ }
157
+ )
158
+ }
159
+ )
160
+ }
161
+ );
162
+ }
163
+ );
164
+ SendButton.displayName = "SendButton";
165
+ var MessageComposer = React6__namespace.forwardRef(
166
+ ({
167
+ onSend,
168
+ onTypingStart,
169
+ onTypingStop,
170
+ value: controlledValue,
171
+ onValueChange,
172
+ placeholder = "Type a message...",
173
+ maxLength = 1600,
174
+ showCharacterCount = false,
175
+ disabled = false,
176
+ isSending = false,
177
+ showAttachmentPicker = true,
178
+ showCameraButton = false,
179
+ acceptedFileTypes = ["image/*", "video/*", ".pdf", ".doc", ".docx"],
180
+ maxFileSize = 25 * 1024 * 1024,
181
+ maxAttachments = 10,
182
+ onError,
183
+ autoFocus = false,
184
+ replyTo = null,
185
+ onCancelReply,
186
+ variant = "default",
187
+ inputTrailing,
188
+ mentionOptions,
189
+ className
190
+ }, ref) => {
191
+ const textareaRef = React6__namespace.useRef(null);
192
+ const [internalContent, setInternalContent] = React6__namespace.useState("");
193
+ const isControlled = controlledValue !== void 0;
194
+ const content = isControlled ? controlledValue : internalContent;
195
+ const setContent = React6__namespace.useCallback(
196
+ (val) => {
197
+ if (isControlled) {
198
+ const newVal = typeof val === "function" ? val(controlledValue) : val;
199
+ onValueChange?.(newVal);
200
+ } else {
201
+ setInternalContent(val);
202
+ }
203
+ },
204
+ [isControlled, controlledValue, onValueChange]
205
+ );
206
+ const [attachments, setAttachments] = React6__namespace.useState(
207
+ []
208
+ );
209
+ const [isTyping, setIsTyping] = React6__namespace.useState(false);
210
+ const typingTimeoutRef = React6__namespace.useRef(null);
211
+ React6__namespace.useImperativeHandle(ref, () => textareaRef.current);
212
+ React6__namespace.useEffect(() => {
213
+ const textarea = textareaRef.current;
214
+ if (textarea) {
215
+ textarea.style.height = "auto";
216
+ textarea.style.height = `${Math.min(textarea.scrollHeight, 150)}px`;
217
+ }
218
+ }, [content]);
219
+ React6__namespace.useEffect(() => {
220
+ if (content.length > 0 && !isTyping) {
221
+ setIsTyping(true);
222
+ onTypingStart?.();
223
+ }
224
+ if (typingTimeoutRef.current) {
225
+ clearTimeout(typingTimeoutRef.current);
226
+ }
227
+ typingTimeoutRef.current = setTimeout(() => {
228
+ if (isTyping) {
229
+ setIsTyping(false);
230
+ onTypingStop?.();
231
+ }
232
+ }, 2e3);
233
+ return () => {
234
+ if (typingTimeoutRef.current) {
235
+ clearTimeout(typingTimeoutRef.current);
236
+ }
237
+ };
238
+ }, [content, isTyping, onTypingStart, onTypingStop]);
239
+ React6__namespace.useEffect(() => {
240
+ if (autoFocus) {
241
+ textareaRef.current?.focus();
242
+ }
243
+ }, [autoFocus]);
244
+ React6__namespace.useEffect(() => {
245
+ if (replyTo) {
246
+ textareaRef.current?.focus();
247
+ }
248
+ }, [replyTo]);
249
+ const mention = chunk5VXRKOPR_cjs.useMentionAutocomplete({
250
+ options: mentionOptions,
251
+ value: content,
252
+ setValue: setContent,
253
+ textareaRef
254
+ });
255
+ const canSend = (content.trim().length > 0 || attachments.length > 0) && content.length <= maxLength && !disabled && !isSending;
256
+ const handleSubmit = async (event) => {
257
+ event.preventDefault();
258
+ if (!canSend) return;
259
+ const message = {
260
+ content: content.trim(),
261
+ attachments: attachments.map((a) => a.file),
262
+ replyToId: replyTo?.id
263
+ };
264
+ setContent("");
265
+ setAttachments([]);
266
+ setIsTyping(false);
267
+ onTypingStop?.();
268
+ try {
269
+ await onSend(message);
270
+ } catch {
271
+ setContent(message.content);
272
+ onError?.("Failed to send message");
273
+ }
274
+ };
275
+ const handleKeyDown = (event) => {
276
+ if (mention.handleKeyDown(event)) return;
277
+ if (event.key === "Enter" && !event.shiftKey) {
278
+ event.preventDefault();
279
+ if (canSend) {
280
+ handleSubmit(event);
281
+ }
282
+ }
283
+ };
284
+ const handleFilesSelected = (files) => {
285
+ const remainingSlots = maxAttachments - attachments.length;
286
+ const filesToAdd = files.slice(0, remainingSlots);
287
+ if (files.length > remainingSlots) {
288
+ onError?.(`Maximum ${maxAttachments} attachments allowed`);
289
+ }
290
+ const newAttachments = filesToAdd.map((file) => {
291
+ const type = chunk5VXRKOPR_cjs.getFileType(file.type);
292
+ let previewUrl;
293
+ if (type === "image" || type === "video") {
294
+ previewUrl = URL.createObjectURL(file);
295
+ }
296
+ return {
297
+ id: chunk5VXRKOPR_cjs.generateAttachmentId(),
298
+ file,
299
+ previewUrl,
300
+ type,
301
+ state: "pending"
302
+ };
303
+ });
304
+ setAttachments((prev) => [...prev, ...newAttachments]);
305
+ };
306
+ const handlePaste = (event) => {
307
+ if (!showAttachmentPicker || disabled) return;
308
+ const pasted = Array.from(event.clipboardData.items).filter((item) => item.kind === "file").map((item) => item.getAsFile()).filter((f) => f !== null).filter((f) => isFileAccepted(f, acceptedFileTypes));
309
+ if (pasted.length === 0) return;
310
+ event.preventDefault();
311
+ handleFilesSelected(pasted);
312
+ };
313
+ const handleRemoveAttachment = (attachmentId) => {
314
+ setAttachments((prev) => {
315
+ const attachment = prev.find((a) => a.id === attachmentId);
316
+ if (attachment?.previewUrl) {
317
+ URL.revokeObjectURL(attachment.previewUrl);
318
+ }
319
+ return prev.filter((a) => a.id !== attachmentId);
320
+ });
321
+ };
322
+ React6__namespace.useEffect(() => {
323
+ const currentAttachments = attachments;
324
+ return () => {
325
+ currentAttachments.forEach((a) => {
326
+ if (a.previewUrl) {
327
+ URL.revokeObjectURL(a.previewUrl);
328
+ }
329
+ });
330
+ };
331
+ }, [attachments]);
332
+ return /* @__PURE__ */ jsxRuntime.jsx(
333
+ chunk5VXRKOPR_cjs.DragDropZone,
334
+ {
335
+ onFilesDropped: handleFilesSelected,
336
+ acceptedTypes: acceptedFileTypes,
337
+ maxFileSize,
338
+ maxFiles: maxAttachments - attachments.length,
339
+ disabled: disabled || attachments.length >= maxAttachments,
340
+ onError,
341
+ className: chunkS5RVSXDV_cjs.cn("w-full", className),
342
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
343
+ "form",
344
+ {
345
+ onSubmit: handleSubmit,
346
+ "data-slot": "message-composer",
347
+ className: "w-full",
348
+ children: [
349
+ replyTo && /* @__PURE__ */ jsxRuntime.jsxs(
350
+ "div",
351
+ {
352
+ "data-slot": "composer-reply-preview",
353
+ className: chunkS5RVSXDV_cjs.cn(
354
+ "flex items-center gap-2 px-4 py-2",
355
+ "bg-neutral-50 dark:bg-neutral-800/50",
356
+ "border-primary-500 border-s-4"
357
+ ),
358
+ children: [
359
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
360
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-primary-800 dark:text-primary-400 text-xs font-medium", children: [
361
+ "Replying to ",
362
+ replyTo.senderName
363
+ ] }),
364
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-sm text-neutral-600 dark:text-neutral-300", children: replyTo.content })
365
+ ] }),
366
+ /* @__PURE__ */ jsxRuntime.jsx(
367
+ "button",
368
+ {
369
+ type: "button",
370
+ onClick: onCancelReply,
371
+ className: chunkS5RVSXDV_cjs.cn(
372
+ "shrink-0 rounded p-1",
373
+ "text-neutral-500 hover:text-neutral-700",
374
+ "dark:text-neutral-400 dark:hover:text-neutral-200",
375
+ "focus:ring-primary-500 focus:ring-2 focus:outline-none"
376
+ ),
377
+ "aria-label": "Cancel reply",
378
+ children: /* @__PURE__ */ jsxRuntime.jsx(
379
+ "svg",
380
+ {
381
+ "aria-hidden": "true",
382
+ className: "h-4 w-4",
383
+ fill: "none",
384
+ viewBox: "0 0 24 24",
385
+ stroke: "currentColor",
386
+ children: /* @__PURE__ */ jsxRuntime.jsx(
387
+ "path",
388
+ {
389
+ strokeLinecap: "round",
390
+ strokeLinejoin: "round",
391
+ strokeWidth: 2,
392
+ d: "M6 18L18 6M6 6l12 12"
393
+ }
394
+ )
395
+ }
396
+ )
397
+ }
398
+ )
399
+ ]
400
+ }
401
+ ),
402
+ attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
403
+ "div",
404
+ {
405
+ "data-slot": "composer-attachments",
406
+ className: chunkS5RVSXDV_cjs.cn(
407
+ "flex flex-wrap gap-2 p-3",
408
+ "border-t border-neutral-200 dark:border-neutral-700"
409
+ ),
410
+ children: attachments.map((attachment) => /* @__PURE__ */ jsxRuntime.jsx(
411
+ chunk5VXRKOPR_cjs.AttachmentPreviewItem,
412
+ {
413
+ attachment,
414
+ onRemove: () => handleRemoveAttachment(attachment.id)
415
+ },
416
+ attachment.id
417
+ ))
418
+ }
419
+ ),
420
+ /* @__PURE__ */ jsxRuntime.jsxs(
421
+ "div",
422
+ {
423
+ "data-slot": "composer-input-area",
424
+ className: chunkS5RVSXDV_cjs.cn(
425
+ "flex items-center gap-2 p-3",
426
+ "bg-white dark:bg-neutral-900",
427
+ variant === "default" && "border-t border-neutral-200 dark:border-neutral-700"
428
+ ),
429
+ children: [
430
+ showAttachmentPicker && /* @__PURE__ */ jsxRuntime.jsx(
431
+ chunk5VXRKOPR_cjs.AttachmentPicker,
432
+ {
433
+ onFilesSelected: handleFilesSelected,
434
+ acceptedTypes: acceptedFileTypes,
435
+ maxFileSize,
436
+ maxFiles: maxAttachments - attachments.length,
437
+ disabled: disabled || attachments.length >= maxAttachments,
438
+ onError
439
+ }
440
+ ),
441
+ showCameraButton && /* @__PURE__ */ jsxRuntime.jsx(
442
+ chunk5VXRKOPR_cjs.CameraButton,
443
+ {
444
+ onCapture: (file) => handleFilesSelected([file]),
445
+ disabled: disabled || attachments.length >= maxAttachments
446
+ }
447
+ ),
448
+ /* @__PURE__ */ jsxRuntime.jsxs(
449
+ "div",
450
+ {
451
+ "data-slot": "composer-input-wrapper",
452
+ className: "relative flex-1",
453
+ ref: mention.anchorRef,
454
+ children: [
455
+ /* @__PURE__ */ jsxRuntime.jsx(chunk5VXRKOPR_cjs.MentionMenu, { mention }),
456
+ /* @__PURE__ */ jsxRuntime.jsx(
457
+ "textarea",
458
+ {
459
+ ref: textareaRef,
460
+ "data-slot": "composer-input",
461
+ value: content,
462
+ onChange: (e) => {
463
+ setContent(e.target.value);
464
+ mention.sync(
465
+ e.target.value,
466
+ e.target.selectionStart ?? e.target.value.length
467
+ );
468
+ },
469
+ onClick: (e) => {
470
+ const el = e.currentTarget;
471
+ mention.sync(el.value, el.selectionStart ?? el.value.length);
472
+ },
473
+ onKeyDown: handleKeyDown,
474
+ onPaste: handlePaste,
475
+ placeholder,
476
+ disabled: disabled || isSending,
477
+ rows: 1,
478
+ className: chunkS5RVSXDV_cjs.cn(
479
+ "w-full resize-none rounded-2xl py-2.5",
480
+ inputTrailing ? "ps-4 pe-10" : "px-4",
481
+ "bg-neutral-100 dark:bg-neutral-800",
482
+ "text-neutral-900 dark:text-neutral-100",
483
+ "placeholder:text-neutral-400 dark:placeholder:text-neutral-500",
484
+ "focus:ring-primary-500 focus:ring-2 focus:outline-none",
485
+ "disabled:cursor-not-allowed disabled:opacity-50",
486
+ "transition-colors",
487
+ "max-h-[150px]"
488
+ ),
489
+ "aria-label": "Message",
490
+ "aria-describedby": showCharacterCount ? "char-count" : void 0,
491
+ ...mention.inputProps
492
+ }
493
+ ),
494
+ inputTrailing && /* @__PURE__ */ jsxRuntime.jsx(
495
+ "div",
496
+ {
497
+ "data-slot": "composer-input-trailing",
498
+ className: "pointer-events-none absolute end-1 top-0 flex h-[44px] items-center [&>*]:pointer-events-auto",
499
+ children: inputTrailing
500
+ }
501
+ ),
502
+ showCharacterCount && /* @__PURE__ */ jsxRuntime.jsx(
503
+ "div",
504
+ {
505
+ "data-slot": "composer-char-count",
506
+ id: "char-count",
507
+ className: "absolute end-3 bottom-1.5",
508
+ children: /* @__PURE__ */ jsxRuntime.jsx(CharacterCounter, { current: content.length, max: maxLength })
509
+ }
510
+ )
511
+ ]
512
+ }
513
+ ),
514
+ /* @__PURE__ */ jsxRuntime.jsx(
515
+ SendButton,
516
+ {
517
+ canSend,
518
+ isLoading: isSending,
519
+ disabled
520
+ }
521
+ )
522
+ ]
523
+ }
524
+ )
525
+ ]
526
+ }
527
+ )
528
+ }
529
+ );
530
+ }
531
+ );
532
+ MessageComposer.displayName = "MessageComposer";
29
533
  var statusIconVariants = classVarianceAuthority.cva(
30
534
  "inline-flex items-center gap-0.5 text-current",
31
535
  {
@@ -406,7 +910,7 @@ function defaultFormatTimestamp(timestamp) {
406
910
  minute: "2-digit"
407
911
  });
408
912
  }
409
- var MessageBubble = React5__namespace.forwardRef(
913
+ var MessageBubble = React6__namespace.forwardRef(
410
914
  ({
411
915
  className,
412
916
  message,
@@ -881,7 +1385,7 @@ function LoadMoreButton({
881
1385
  );
882
1386
  }
883
1387
  LoadMoreButton.displayName = "LoadMoreButton";
884
- var MessageList = React5__namespace.forwardRef(
1388
+ var MessageList = React6__namespace.forwardRef(
885
1389
  ({
886
1390
  messages,
887
1391
  currentUser,
@@ -900,19 +1404,19 @@ var MessageList = React5__namespace.forwardRef(
900
1404
  className,
901
1405
  autoScroll = "onNewMessage"
902
1406
  }, ref) => {
903
- const scrollContainerRef = React5__namespace.useRef(null);
904
- const bottomRef = React5__namespace.useRef(null);
905
- const [isUserScrolled, setIsUserScrolled] = React5__namespace.useState(false);
906
- const prevMessageCountRef = React5__namespace.useRef(messages.length);
907
- React5__namespace.useImperativeHandle(ref, () => scrollContainerRef.current);
908
- const handleScroll = React5__namespace.useCallback(() => {
1407
+ const scrollContainerRef = React6__namespace.useRef(null);
1408
+ const bottomRef = React6__namespace.useRef(null);
1409
+ const [isUserScrolled, setIsUserScrolled] = React6__namespace.useState(false);
1410
+ const prevMessageCountRef = React6__namespace.useRef(messages.length);
1411
+ React6__namespace.useImperativeHandle(ref, () => scrollContainerRef.current);
1412
+ const handleScroll = React6__namespace.useCallback(() => {
909
1413
  const container = scrollContainerRef.current;
910
1414
  if (!container) return;
911
1415
  const { scrollTop, scrollHeight, clientHeight } = container;
912
1416
  const isAtBottom = scrollHeight - scrollTop - clientHeight < 100;
913
1417
  setIsUserScrolled(!isAtBottom);
914
1418
  }, []);
915
- React5__namespace.useEffect(() => {
1419
+ React6__namespace.useEffect(() => {
916
1420
  const container = scrollContainerRef.current;
917
1421
  const bottom = bottomRef.current;
918
1422
  if (!container || !bottom) return;
@@ -928,7 +1432,7 @@ var MessageList = React5__namespace.forwardRef(
928
1432
  }
929
1433
  }
930
1434
  }, [messages, currentUser.id, autoScroll, isUserScrolled]);
931
- React5__namespace.useEffect(() => {
1435
+ React6__namespace.useEffect(() => {
932
1436
  const bottom = bottomRef.current;
933
1437
  if (bottom && !isLoading) {
934
1438
  bottom.scrollIntoView();
@@ -981,7 +1485,7 @@ var MessageList = React5__namespace.forwardRef(
981
1485
  {
982
1486
  "data-slot": "message-list-content",
983
1487
  className: "flex flex-col gap-1 p-4",
984
- children: messageGroups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs(React5__namespace.Fragment, { children: [
1488
+ children: messageGroups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs(React6__namespace.Fragment, { children: [
985
1489
  groupByDate && group.label && /* @__PURE__ */ jsxRuntime.jsx(DateSeparator, { label: group.label }),
986
1490
  group.messages.map((message, index) => {
987
1491
  const prevMessage = group.messages[index - 1];
@@ -1120,7 +1624,7 @@ function formatLastSeen(date) {
1120
1624
  if (diffDays < 7) return `${diffDays}d ago`;
1121
1625
  return date.toLocaleDateString();
1122
1626
  }
1123
- var ConversationHeader = React5__namespace.forwardRef(
1627
+ var ConversationHeader = React6__namespace.forwardRef(
1124
1628
  ({
1125
1629
  className,
1126
1630
  size,
@@ -1242,7 +1746,7 @@ var ConversationHeader = React5__namespace.forwardRef(
1242
1746
  }
1243
1747
  );
1244
1748
  ConversationHeader.displayName = "ConversationHeader";
1245
- var ConversationListItem = React5__namespace.forwardRef(({ className, conversation, isSelected, onSelect, ...props }, ref) => {
1749
+ var ConversationListItem = React6__namespace.forwardRef(({ className, conversation, isSelected, onSelect, ...props }, ref) => {
1246
1750
  const participant = conversation.participants.find((p) => !p.isCurrentUser);
1247
1751
  const title = getConversationTitle(conversation, participant);
1248
1752
  const avatarUrl = conversation.avatarUrl || participant?.avatarUrl;
@@ -1450,7 +1954,7 @@ function ConversationListSkeleton({
1450
1954
  }
1451
1955
  ConversationListSkeleton.displayName = "ConversationListSkeleton";
1452
1956
  function LightboxModal({ attachment, onClose }) {
1453
- React5__namespace.useEffect(() => {
1957
+ React6__namespace.useEffect(() => {
1454
1958
  const handleKeyDown = (event) => {
1455
1959
  if (event.key === "Escape") {
1456
1960
  onClose();
@@ -1547,7 +2051,7 @@ function LightboxModal({ attachment, onClose }) {
1547
2051
  );
1548
2052
  }
1549
2053
  LightboxModal.displayName = "LightboxModal";
1550
- var MessageThread = React5__namespace.forwardRef(
2054
+ var MessageThread = React6__namespace.forwardRef(
1551
2055
  ({
1552
2056
  conversation,
1553
2057
  messages,
@@ -1578,8 +2082,8 @@ var MessageThread = React5__namespace.forwardRef(
1578
2082
  onError,
1579
2083
  className
1580
2084
  }, ref) => {
1581
- const [lightboxAttachment, setLightboxAttachment] = React5__namespace.useState(null);
1582
- const [replyTo, setReplyTo] = React5__namespace.useState(null);
2085
+ const [lightboxAttachment, setLightboxAttachment] = React6__namespace.useState(null);
2086
+ const [replyTo, setReplyTo] = React6__namespace.useState(null);
1583
2087
  const participant = conversation?.type === "direct" ? conversation.participants.find((p) => p.id !== currentUser.id) : void 0;
1584
2088
  const handleAttachmentClick = (attachment, message) => {
1585
2089
  if (attachment.type === "image" || attachment.type === "video") {
@@ -1617,7 +2121,7 @@ var MessageThread = React5__namespace.forwardRef(
1617
2121
  }
1618
2122
  ),
1619
2123
  /* @__PURE__ */ jsxRuntime.jsx(
1620
- chunk5BVFVXHN_cjs.DragDropZone,
2124
+ chunk5VXRKOPR_cjs.DragDropZone,
1621
2125
  {
1622
2126
  onFilesDropped: () => {
1623
2127
  onError?.("Drop files on the composer to attach them");
@@ -1647,7 +2151,7 @@ var MessageThread = React5__namespace.forwardRef(
1647
2151
  }
1648
2152
  ),
1649
2153
  /* @__PURE__ */ jsxRuntime.jsx(
1650
- chunk5BVFVXHN_cjs.MessageComposer,
2154
+ MessageComposer,
1651
2155
  {
1652
2156
  onSend: handleSendMessage,
1653
2157
  onTypingStart: eventHandlers.onTypingStart,
@@ -1751,13 +2255,13 @@ function useMessages(options) {
1751
2255
  onRetry,
1752
2256
  onLoadMore
1753
2257
  } = options;
1754
- const [messages, setMessages] = React5__namespace.useState(initialMessages);
1755
- const [isSending, setIsSending] = React5__namespace.useState(false);
1756
- const [isLoadingMore, setIsLoadingMore] = React5__namespace.useState(false);
1757
- React5__namespace.useEffect(() => {
2258
+ const [messages, setMessages] = React6__namespace.useState(initialMessages);
2259
+ const [isSending, setIsSending] = React6__namespace.useState(false);
2260
+ const [isLoadingMore, setIsLoadingMore] = React6__namespace.useState(false);
2261
+ React6__namespace.useEffect(() => {
1758
2262
  setMessages(initialMessages);
1759
2263
  }, [initialMessages]);
1760
- const addMessage = React5__namespace.useCallback((message) => {
2264
+ const addMessage = React6__namespace.useCallback((message) => {
1761
2265
  setMessages((prev) => {
1762
2266
  if (prev.some((m) => m.id === message.id)) {
1763
2267
  return prev;
@@ -1765,7 +2269,7 @@ function useMessages(options) {
1765
2269
  return [...prev, message];
1766
2270
  });
1767
2271
  }, []);
1768
- const updateMessage = React5__namespace.useCallback(
2272
+ const updateMessage = React6__namespace.useCallback(
1769
2273
  (messageId, updates) => {
1770
2274
  setMessages(
1771
2275
  (prev) => prev.map((m) => m.id === messageId ? { ...m, ...updates } : m)
@@ -1773,22 +2277,22 @@ function useMessages(options) {
1773
2277
  },
1774
2278
  []
1775
2279
  );
1776
- const removeMessage = React5__namespace.useCallback((messageId) => {
2280
+ const removeMessage = React6__namespace.useCallback((messageId) => {
1777
2281
  setMessages((prev) => prev.filter((m) => m.id !== messageId));
1778
2282
  }, []);
1779
- const updateStatus = React5__namespace.useCallback(
2283
+ const updateStatus = React6__namespace.useCallback(
1780
2284
  (messageId, status) => {
1781
2285
  updateMessage(messageId, { status });
1782
2286
  },
1783
2287
  [updateMessage]
1784
2288
  );
1785
- const markAsRead = React5__namespace.useCallback(
2289
+ const markAsRead = React6__namespace.useCallback(
1786
2290
  (messageId) => {
1787
2291
  updateStatus(messageId, "read");
1788
2292
  },
1789
2293
  [updateStatus]
1790
2294
  );
1791
- const sendMessage = React5__namespace.useCallback(
2295
+ const sendMessage = React6__namespace.useCallback(
1792
2296
  async (newMessage) => {
1793
2297
  const optimisticId = `optimistic-${Date.now()}`;
1794
2298
  const optimisticMessage = {
@@ -1820,7 +2324,7 @@ function useMessages(options) {
1820
2324
  },
1821
2325
  [currentUser, onSend, addMessage, updateStatus]
1822
2326
  );
1823
- const retryMessage = React5__namespace.useCallback(
2327
+ const retryMessage = React6__namespace.useCallback(
1824
2328
  async (messageId) => {
1825
2329
  updateStatus(messageId, "sending");
1826
2330
  try {
@@ -1834,7 +2338,7 @@ function useMessages(options) {
1834
2338
  },
1835
2339
  [onRetry, updateStatus]
1836
2340
  );
1837
- const loadMore = React5__namespace.useCallback(async () => {
2341
+ const loadMore = React6__namespace.useCallback(async () => {
1838
2342
  if (isLoadingMore || !onLoadMore) return;
1839
2343
  setIsLoadingMore(true);
1840
2344
  try {
@@ -1865,13 +2369,13 @@ function useTypingIndicator(options = {}) {
1865
2369
  onTypingStart,
1866
2370
  onTypingStop
1867
2371
  } = options;
1868
- const [participants, setParticipants] = React5__namespace.useState(initialParticipants);
1869
- const [isLocalTyping, setIsLocalTyping] = React5__namespace.useState(false);
1870
- const typingTimeoutRef = React5__namespace.useRef(null);
1871
- React5__namespace.useEffect(() => {
2372
+ const [participants, setParticipants] = React6__namespace.useState(initialParticipants);
2373
+ const [isLocalTyping, setIsLocalTyping] = React6__namespace.useState(false);
2374
+ const typingTimeoutRef = React6__namespace.useRef(null);
2375
+ React6__namespace.useEffect(() => {
1872
2376
  setParticipants(initialParticipants);
1873
2377
  }, [initialParticipants]);
1874
- const startTyping = React5__namespace.useCallback(() => {
2378
+ const startTyping = React6__namespace.useCallback(() => {
1875
2379
  if (!isLocalTyping) {
1876
2380
  setIsLocalTyping(true);
1877
2381
  onTypingStart?.();
@@ -1884,21 +2388,21 @@ function useTypingIndicator(options = {}) {
1884
2388
  onTypingStop?.();
1885
2389
  }, debounceTime);
1886
2390
  }, [isLocalTyping, debounceTime, onTypingStart, onTypingStop]);
1887
- const stopTyping = React5__namespace.useCallback(() => {
2391
+ const stopTyping = React6__namespace.useCallback(() => {
1888
2392
  if (typingTimeoutRef.current) {
1889
2393
  clearTimeout(typingTimeoutRef.current);
1890
2394
  }
1891
2395
  setIsLocalTyping(false);
1892
2396
  onTypingStop?.();
1893
2397
  }, [onTypingStop]);
1894
- React5__namespace.useEffect(() => {
2398
+ React6__namespace.useEffect(() => {
1895
2399
  return () => {
1896
2400
  if (typingTimeoutRef.current) {
1897
2401
  clearTimeout(typingTimeoutRef.current);
1898
2402
  }
1899
2403
  };
1900
2404
  }, []);
1901
- const typingState = React5__namespace.useMemo(
2405
+ const typingState = React6__namespace.useMemo(
1902
2406
  () => ({
1903
2407
  participants,
1904
2408
  lastUpdated: /* @__PURE__ */ new Date()
@@ -1914,11 +2418,11 @@ function useTypingIndicator(options = {}) {
1914
2418
  }
1915
2419
  function useMessageScroll(options) {
1916
2420
  const { messages, currentUserId, threshold = 100 } = options;
1917
- const scrollContainerRef = React5__namespace.useRef(null);
1918
- const bottomRef = React5__namespace.useRef(null);
1919
- const [isScrolledUp, setIsScrolledUp] = React5__namespace.useState(false);
1920
- const prevMessageCountRef = React5__namespace.useRef(messages.length);
1921
- React5__namespace.useEffect(() => {
2421
+ const scrollContainerRef = React6__namespace.useRef(null);
2422
+ const bottomRef = React6__namespace.useRef(null);
2423
+ const [isScrolledUp, setIsScrolledUp] = React6__namespace.useState(false);
2424
+ const prevMessageCountRef = React6__namespace.useRef(messages.length);
2425
+ React6__namespace.useEffect(() => {
1922
2426
  const container = scrollContainerRef.current;
1923
2427
  if (!container) return;
1924
2428
  const handleScroll = () => {
@@ -1929,12 +2433,12 @@ function useMessageScroll(options) {
1929
2433
  container.addEventListener("scroll", handleScroll);
1930
2434
  return () => container.removeEventListener("scroll", handleScroll);
1931
2435
  }, [threshold]);
1932
- const scrollToBottom = React5__namespace.useCallback((smooth = true) => {
2436
+ const scrollToBottom = React6__namespace.useCallback((smooth = true) => {
1933
2437
  bottomRef.current?.scrollIntoView({
1934
2438
  behavior: smooth ? "smooth" : "auto"
1935
2439
  });
1936
2440
  }, []);
1937
- React5__namespace.useEffect(() => {
2441
+ React6__namespace.useEffect(() => {
1938
2442
  const messageCountChanged = messages.length !== prevMessageCountRef.current;
1939
2443
  prevMessageCountRef.current = messages.length;
1940
2444
  if (!messageCountChanged) return;
@@ -1953,9 +2457,9 @@ function useMessageScroll(options) {
1953
2457
  }
1954
2458
  function useReadReceipts(options) {
1955
2459
  const { currentUserId, onMarkRead, threshold = 0.5 } = options;
1956
- const observerRef = React5__namespace.useRef(null);
1957
- const observedMessagesRef = React5__namespace.useRef(/* @__PURE__ */ new Set());
1958
- React5__namespace.useEffect(() => {
2460
+ const observerRef = React6__namespace.useRef(null);
2461
+ const observedMessagesRef = React6__namespace.useRef(/* @__PURE__ */ new Set());
2462
+ React6__namespace.useEffect(() => {
1959
2463
  observerRef.current = new IntersectionObserver(
1960
2464
  (entries) => {
1961
2465
  entries.forEach((entry) => {
@@ -1974,7 +2478,7 @@ function useReadReceipts(options) {
1974
2478
  observerRef.current?.disconnect();
1975
2479
  };
1976
2480
  }, [onMarkRead, threshold]);
1977
- const observeMessage = React5__namespace.useCallback(
2481
+ const observeMessage = React6__namespace.useCallback(
1978
2482
  (element, message) => {
1979
2483
  if (!element || !observerRef.current) return;
1980
2484
  if (message.sender.id !== currentUserId && message.status !== "read" && !observedMessagesRef.current.has(message.id)) {
@@ -1988,6 +2492,7 @@ function useReadReceipts(options) {
1988
2492
  }
1989
2493
 
1990
2494
  exports.AttachmentPreview = AttachmentPreview;
2495
+ exports.CharacterCounter = CharacterCounter;
1991
2496
  exports.ConversationHeader = ConversationHeader;
1992
2497
  exports.ConversationListItem = ConversationListItem;
1993
2498
  exports.ConversationListSkeleton = ConversationListSkeleton;
@@ -1996,11 +2501,13 @@ exports.EmptyState = EmptyState;
1996
2501
  exports.LightboxModal = LightboxModal;
1997
2502
  exports.LoadMoreButton = LoadMoreButton;
1998
2503
  exports.MessageBubble = MessageBubble;
2504
+ exports.MessageComposer = MessageComposer;
1999
2505
  exports.MessageList = MessageList;
2000
2506
  exports.MessageStatusIcon = MessageStatusIcon;
2001
2507
  exports.MessageThread = MessageThread;
2002
2508
  exports.MessagingSplitView = MessagingSplitView;
2003
2509
  exports.ReadReceiptIndicator = ReadReceiptIndicator;
2510
+ exports.SendButton = SendButton;
2004
2511
  exports.SkeletonMessage = SkeletonMessage;
2005
2512
  exports.TypingIndicator = TypingIndicator;
2006
2513
  exports.bubbleVariants = bubbleVariants;
@@ -2012,9 +2519,10 @@ exports.getConversationTitle = getConversationTitle;
2012
2519
  exports.groupMessagesByDate = groupMessagesByDate;
2013
2520
  exports.headerVariants = headerVariants;
2014
2521
  exports.isSameSenderGroup = isSameSenderGroup;
2522
+ exports.sendButtonVariants = sendButtonVariants;
2015
2523
  exports.useMessageScroll = useMessageScroll;
2016
2524
  exports.useMessages = useMessages;
2017
2525
  exports.useReadReceipts = useReadReceipts;
2018
2526
  exports.useTypingIndicator = useTypingIndicator;
2019
- //# sourceMappingURL=chunk-FQBTGS3E.cjs.map
2020
- //# sourceMappingURL=chunk-FQBTGS3E.cjs.map
2527
+ //# sourceMappingURL=chunk-O425FKDI.cjs.map
2528
+ //# sourceMappingURL=chunk-O425FKDI.cjs.map