@mieweb/ui 0.9.0-dev.5 → 0.9.0-dev.6

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 (33) hide show
  1. package/dist/{chunk-QWJGEDHV.js → chunk-22CYDCXA.js} +13 -1183
  2. package/dist/chunk-22CYDCXA.js.map +1 -0
  3. package/dist/chunk-3SD7PDCV.js +1878 -0
  4. package/dist/chunk-3SD7PDCV.js.map +1 -0
  5. package/dist/{chunk-PL6TFCYR.js → chunk-NORSXAXL.js} +374 -300
  6. package/dist/chunk-NORSXAXL.js.map +1 -0
  7. package/dist/chunk-T6PTKWM6.cjs +1912 -0
  8. package/dist/chunk-T6PTKWM6.cjs.map +1 -0
  9. package/dist/{chunk-XJUIZA4G.cjs → chunk-WIAQQNFU.cjs} +12 -1184
  10. package/dist/chunk-WIAQQNFU.cjs.map +1 -0
  11. package/dist/{chunk-ZXRVVNT5.cjs → chunk-XCNDZNY7.cjs} +384 -309
  12. package/dist/chunk-XCNDZNY7.cjs.map +1 -0
  13. package/dist/components/SuperChat/index.cjs +8 -8
  14. package/dist/components/SuperChat/index.js +2 -2
  15. package/dist/components/SuperChat/plugins/index.cjs +10 -3
  16. package/dist/components/SuperChat/plugins/index.cjs.map +1 -1
  17. package/dist/components/SuperChat/plugins/index.js +9 -2
  18. package/dist/components/SuperChat/plugins/index.js.map +1 -1
  19. package/dist/index.cjs +92 -120
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.d.cts +38 -4
  22. package/dist/index.d.ts +38 -4
  23. package/dist/index.js +17 -48
  24. package/dist/index.js.map +1 -1
  25. package/package.json +1 -1
  26. package/dist/chunk-H3GRO43Z.cjs +0 -735
  27. package/dist/chunk-H3GRO43Z.cjs.map +0 -1
  28. package/dist/chunk-MD3ZY4UO.js +0 -705
  29. package/dist/chunk-MD3ZY4UO.js.map +0 -1
  30. package/dist/chunk-PL6TFCYR.js.map +0 -1
  31. package/dist/chunk-QWJGEDHV.js.map +0 -1
  32. package/dist/chunk-XJUIZA4G.cjs.map +0 -1
  33. package/dist/chunk-ZXRVVNT5.cjs.map +0 -1
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var chunkH3GRO43Z_cjs = require('./chunk-H3GRO43Z.cjs');
3
+ var chunkT6PTKWM6_cjs = require('./chunk-T6PTKWM6.cjs');
4
4
  var chunkS5RVSXDV_cjs = require('./chunk-S5RVSXDV.cjs');
5
- var React6 = require('react');
5
+ var React = require('react');
6
6
  var classVarianceAuthority = require('class-variance-authority');
7
7
  var jsxRuntime = require('react/jsx-runtime');
8
8
 
@@ -24,8 +24,287 @@ function _interopNamespace(e) {
24
24
  return Object.freeze(n);
25
25
  }
26
26
 
27
- var React6__namespace = /*#__PURE__*/_interopNamespace(React6);
27
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
28
28
 
29
+ function useMessages(options) {
30
+ const {
31
+ initialMessages = [],
32
+ currentUser,
33
+ onSend,
34
+ onRetry,
35
+ onLoadMore
36
+ } = options;
37
+ const [messages, setMessages] = React__namespace.useState(initialMessages);
38
+ const [isSending, setIsSending] = React__namespace.useState(false);
39
+ const [isLoadingMore, setIsLoadingMore] = React__namespace.useState(false);
40
+ React__namespace.useEffect(() => {
41
+ setMessages(initialMessages);
42
+ }, [initialMessages]);
43
+ const addMessage = React__namespace.useCallback((message) => {
44
+ setMessages((prev) => {
45
+ if (prev.some((m) => m.id === message.id)) {
46
+ return prev;
47
+ }
48
+ return [...prev, message];
49
+ });
50
+ }, []);
51
+ const updateMessage = React__namespace.useCallback(
52
+ (messageId, updates) => {
53
+ setMessages(
54
+ (prev) => prev.map((m) => m.id === messageId ? { ...m, ...updates } : m)
55
+ );
56
+ },
57
+ []
58
+ );
59
+ const removeMessage = React__namespace.useCallback((messageId) => {
60
+ setMessages((prev) => prev.filter((m) => m.id !== messageId));
61
+ }, []);
62
+ const updateStatus = React__namespace.useCallback(
63
+ (messageId, status) => {
64
+ updateMessage(messageId, { status });
65
+ },
66
+ [updateMessage]
67
+ );
68
+ const markAsRead = React__namespace.useCallback(
69
+ (messageId) => {
70
+ updateStatus(messageId, "read");
71
+ },
72
+ [updateStatus]
73
+ );
74
+ const sendMessage = React__namespace.useCallback(
75
+ async (newMessage) => {
76
+ const optimisticId = `optimistic-${Date.now()}`;
77
+ const optimisticMessage = {
78
+ id: optimisticId,
79
+ type: "text",
80
+ content: newMessage.content,
81
+ sender: currentUser,
82
+ timestamp: /* @__PURE__ */ new Date(),
83
+ status: "sending",
84
+ attachments: []
85
+ // Would handle attachment uploads here
86
+ };
87
+ addMessage(optimisticMessage);
88
+ setIsSending(true);
89
+ try {
90
+ if (onSend) {
91
+ const sentMessage = await onSend(newMessage);
92
+ setMessages(
93
+ (prev) => prev.map((m) => m.id === optimisticId ? sentMessage : m)
94
+ );
95
+ } else {
96
+ updateStatus(optimisticId, "sent");
97
+ }
98
+ } catch {
99
+ updateStatus(optimisticId, "failed");
100
+ } finally {
101
+ setIsSending(false);
102
+ }
103
+ },
104
+ [currentUser, onSend, addMessage, updateStatus]
105
+ );
106
+ const retryMessage = React__namespace.useCallback(
107
+ async (messageId) => {
108
+ updateStatus(messageId, "sending");
109
+ try {
110
+ if (onRetry) {
111
+ await onRetry(messageId);
112
+ updateStatus(messageId, "sent");
113
+ }
114
+ } catch {
115
+ updateStatus(messageId, "failed");
116
+ }
117
+ },
118
+ [onRetry, updateStatus]
119
+ );
120
+ const loadMore = React__namespace.useCallback(async () => {
121
+ if (isLoadingMore || !onLoadMore) return;
122
+ setIsLoadingMore(true);
123
+ try {
124
+ const olderMessages = await onLoadMore();
125
+ setMessages((prev) => [...olderMessages, ...prev]);
126
+ } finally {
127
+ setIsLoadingMore(false);
128
+ }
129
+ }, [isLoadingMore, onLoadMore]);
130
+ return {
131
+ messages,
132
+ addMessage,
133
+ updateMessage,
134
+ removeMessage,
135
+ sendMessage,
136
+ retryMessage,
137
+ loadMore,
138
+ isSending,
139
+ isLoadingMore,
140
+ markAsRead,
141
+ updateStatus
142
+ };
143
+ }
144
+ function useTypingIndicator(options = {}) {
145
+ const {
146
+ typingParticipants: initialParticipants = [],
147
+ debounceTime = 2e3,
148
+ onTypingStart,
149
+ onTypingStop
150
+ } = options;
151
+ const [participants, setParticipants] = React__namespace.useState(initialParticipants);
152
+ const [isLocalTyping, setIsLocalTyping] = React__namespace.useState(false);
153
+ const typingTimeoutRef = React__namespace.useRef(null);
154
+ React__namespace.useEffect(() => {
155
+ setParticipants(initialParticipants);
156
+ }, [initialParticipants]);
157
+ const startTyping = React__namespace.useCallback(() => {
158
+ if (!isLocalTyping) {
159
+ setIsLocalTyping(true);
160
+ onTypingStart?.();
161
+ }
162
+ if (typingTimeoutRef.current) {
163
+ clearTimeout(typingTimeoutRef.current);
164
+ }
165
+ typingTimeoutRef.current = setTimeout(() => {
166
+ setIsLocalTyping(false);
167
+ onTypingStop?.();
168
+ }, debounceTime);
169
+ }, [isLocalTyping, debounceTime, onTypingStart, onTypingStop]);
170
+ const stopTyping = React__namespace.useCallback(() => {
171
+ if (typingTimeoutRef.current) {
172
+ clearTimeout(typingTimeoutRef.current);
173
+ }
174
+ setIsLocalTyping(false);
175
+ onTypingStop?.();
176
+ }, [onTypingStop]);
177
+ React__namespace.useEffect(() => {
178
+ return () => {
179
+ if (typingTimeoutRef.current) {
180
+ clearTimeout(typingTimeoutRef.current);
181
+ }
182
+ };
183
+ }, []);
184
+ const typingState = React__namespace.useMemo(
185
+ () => ({
186
+ participants,
187
+ lastUpdated: /* @__PURE__ */ new Date()
188
+ }),
189
+ [participants]
190
+ );
191
+ return {
192
+ typingState,
193
+ startTyping,
194
+ stopTyping,
195
+ setTypingParticipants: setParticipants
196
+ };
197
+ }
198
+ function useMessageScroll(options) {
199
+ const { messages, currentUserId, threshold = 100 } = options;
200
+ const scrollContainerRef = React__namespace.useRef(null);
201
+ const bottomRef = React__namespace.useRef(null);
202
+ const [isScrolledUp, setIsScrolledUp] = React__namespace.useState(false);
203
+ const prevMessageCountRef = React__namespace.useRef(messages.length);
204
+ React__namespace.useEffect(() => {
205
+ const container = scrollContainerRef.current;
206
+ if (!container) return;
207
+ const handleScroll = () => {
208
+ const { scrollTop, scrollHeight, clientHeight } = container;
209
+ const isAtBottom = scrollHeight - scrollTop - clientHeight < threshold;
210
+ setIsScrolledUp(!isAtBottom);
211
+ };
212
+ container.addEventListener("scroll", handleScroll);
213
+ return () => container.removeEventListener("scroll", handleScroll);
214
+ }, [threshold]);
215
+ const scrollToBottom = React__namespace.useCallback((smooth = true) => {
216
+ bottomRef.current?.scrollIntoView({
217
+ behavior: smooth ? "smooth" : "auto"
218
+ });
219
+ }, []);
220
+ React__namespace.useEffect(() => {
221
+ const messageCountChanged = messages.length !== prevMessageCountRef.current;
222
+ prevMessageCountRef.current = messages.length;
223
+ if (!messageCountChanged) return;
224
+ const lastMessage = messages[messages.length - 1];
225
+ const isOutgoing = lastMessage?.sender.id === currentUserId;
226
+ if (isOutgoing || !isScrolledUp) {
227
+ scrollToBottom(true);
228
+ }
229
+ }, [messages, currentUserId, isScrolledUp, scrollToBottom]);
230
+ return {
231
+ scrollContainerRef,
232
+ bottomRef,
233
+ isScrolledUp,
234
+ scrollToBottom
235
+ };
236
+ }
237
+ function useReadReceipts(options) {
238
+ const { currentUserId, onMarkRead, threshold = 0.5 } = options;
239
+ const observerRef = React__namespace.useRef(null);
240
+ const observedMessagesRef = React__namespace.useRef(/* @__PURE__ */ new Set());
241
+ React__namespace.useEffect(() => {
242
+ observerRef.current = new IntersectionObserver(
243
+ (entries) => {
244
+ entries.forEach((entry) => {
245
+ if (entry.isIntersecting) {
246
+ const messageId = entry.target.getAttribute("data-message-id");
247
+ if (messageId && !observedMessagesRef.current.has(messageId)) {
248
+ observedMessagesRef.current.add(messageId);
249
+ onMarkRead?.(messageId);
250
+ }
251
+ }
252
+ });
253
+ },
254
+ { threshold }
255
+ );
256
+ return () => {
257
+ observerRef.current?.disconnect();
258
+ };
259
+ }, [onMarkRead, threshold]);
260
+ const observeMessage = React__namespace.useCallback(
261
+ (element, message) => {
262
+ if (!element || !observerRef.current) return;
263
+ if (message.sender.id !== currentUserId && message.status !== "read" && !observedMessagesRef.current.has(message.id)) {
264
+ element.setAttribute("data-message-id", message.id);
265
+ observerRef.current.observe(element);
266
+ }
267
+ },
268
+ [currentUserId]
269
+ );
270
+ return { observeMessage };
271
+ }
272
+ function useTypingEmulation({
273
+ value,
274
+ onTypingStart,
275
+ onTypingStop
276
+ }) {
277
+ const [isTyping, setIsTyping] = React__namespace.useState(false);
278
+ const typingTimeoutRef = React__namespace.useRef(
279
+ null
280
+ );
281
+ React__namespace.useEffect(() => {
282
+ if (!onTypingStart && !onTypingStop) return;
283
+ if (value.length > 0 && !isTyping) {
284
+ setIsTyping(true);
285
+ onTypingStart?.();
286
+ }
287
+ if (typingTimeoutRef.current) {
288
+ clearTimeout(typingTimeoutRef.current);
289
+ }
290
+ typingTimeoutRef.current = setTimeout(() => {
291
+ if (isTyping) {
292
+ setIsTyping(false);
293
+ onTypingStop?.();
294
+ }
295
+ }, 2e3);
296
+ return () => {
297
+ if (typingTimeoutRef.current) {
298
+ clearTimeout(typingTimeoutRef.current);
299
+ }
300
+ };
301
+ }, [value, isTyping, onTypingStart, onTypingStop]);
302
+ const stopTyping = React__namespace.useCallback(() => {
303
+ setIsTyping(false);
304
+ onTypingStop?.();
305
+ }, [onTypingStop]);
306
+ return { stopTyping };
307
+ }
29
308
  var statusIconVariants = classVarianceAuthority.cva(
30
309
  "inline-flex items-center gap-0.5 text-current",
31
310
  {
@@ -406,7 +685,7 @@ function defaultFormatTimestamp(timestamp) {
406
685
  minute: "2-digit"
407
686
  });
408
687
  }
409
- var MessageBubble = React6__namespace.forwardRef(
688
+ var MessageBubble = React__namespace.forwardRef(
410
689
  ({
411
690
  className,
412
691
  message,
@@ -881,7 +1160,7 @@ function LoadMoreButton({
881
1160
  );
882
1161
  }
883
1162
  LoadMoreButton.displayName = "LoadMoreButton";
884
- var MessageList = React6__namespace.forwardRef(
1163
+ var MessageList = React__namespace.forwardRef(
885
1164
  ({
886
1165
  messages,
887
1166
  currentUser,
@@ -900,19 +1179,19 @@ var MessageList = React6__namespace.forwardRef(
900
1179
  className,
901
1180
  autoScroll = "onNewMessage"
902
1181
  }, ref) => {
903
- const scrollContainerRef = React6__namespace.useRef(null);
904
- const bottomRef = React6__namespace.useRef(null);
905
- const [isUserScrolled, setIsUserScrolled] = React6__namespace.useState(false);
906
- const prevMessageCountRef = React6__namespace.useRef(messages.length);
907
- React6__namespace.useImperativeHandle(ref, () => scrollContainerRef.current);
908
- const handleScroll = React6__namespace.useCallback(() => {
1182
+ const scrollContainerRef = React__namespace.useRef(null);
1183
+ const bottomRef = React__namespace.useRef(null);
1184
+ const [isUserScrolled, setIsUserScrolled] = React__namespace.useState(false);
1185
+ const prevMessageCountRef = React__namespace.useRef(messages.length);
1186
+ React__namespace.useImperativeHandle(ref, () => scrollContainerRef.current);
1187
+ const handleScroll = React__namespace.useCallback(() => {
909
1188
  const container = scrollContainerRef.current;
910
1189
  if (!container) return;
911
1190
  const { scrollTop, scrollHeight, clientHeight } = container;
912
1191
  const isAtBottom = scrollHeight - scrollTop - clientHeight < 100;
913
1192
  setIsUserScrolled(!isAtBottom);
914
1193
  }, []);
915
- React6__namespace.useEffect(() => {
1194
+ React__namespace.useEffect(() => {
916
1195
  const container = scrollContainerRef.current;
917
1196
  const bottom = bottomRef.current;
918
1197
  if (!container || !bottom) return;
@@ -928,7 +1207,7 @@ var MessageList = React6__namespace.forwardRef(
928
1207
  }
929
1208
  }
930
1209
  }, [messages, currentUser.id, autoScroll, isUserScrolled]);
931
- React6__namespace.useEffect(() => {
1210
+ React__namespace.useEffect(() => {
932
1211
  const bottom = bottomRef.current;
933
1212
  if (bottom && !isLoading) {
934
1213
  bottom.scrollIntoView();
@@ -981,7 +1260,7 @@ var MessageList = React6__namespace.forwardRef(
981
1260
  {
982
1261
  "data-slot": "message-list-content",
983
1262
  className: "flex flex-col gap-1 p-4",
984
- children: messageGroups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs(React6__namespace.Fragment, { children: [
1263
+ children: messageGroups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Fragment, { children: [
985
1264
  groupByDate && group.label && /* @__PURE__ */ jsxRuntime.jsx(DateSeparator, { label: group.label }),
986
1265
  group.messages.map((message, index) => {
987
1266
  const prevMessage = group.messages[index - 1];
@@ -1132,7 +1411,7 @@ var sendButtonVariants = classVarianceAuthority.cva(
1132
1411
  }
1133
1412
  }
1134
1413
  );
1135
- var SendButton = React6__namespace.forwardRef(
1414
+ var SendButton = React__namespace.forwardRef(
1136
1415
  ({ className, variant, canSend, isLoading, disabled, ...props }, ref) => {
1137
1416
  return /* @__PURE__ */ jsxRuntime.jsx(
1138
1417
  "button",
@@ -1197,7 +1476,7 @@ var SendButton = React6__namespace.forwardRef(
1197
1476
  }
1198
1477
  );
1199
1478
  SendButton.displayName = "SendButton";
1200
- var MessageComposer = React6__namespace.forwardRef(
1479
+ var MessageComposer = React__namespace.forwardRef(
1201
1480
  ({
1202
1481
  onSend,
1203
1482
  onTypingStart,
@@ -1211,8 +1490,8 @@ var MessageComposer = React6__namespace.forwardRef(
1211
1490
  isSending = false,
1212
1491
  showAttachmentPicker = true,
1213
1492
  showCameraButton = false,
1214
- acceptedFileTypes = ["image/*", "video/*", ".pdf", ".doc", ".docx"],
1215
- maxFileSize = 25 * 1024 * 1024,
1493
+ acceptedFileTypes = chunkT6PTKWM6_cjs.DEFAULT_ACCEPTED_FILE_TYPES,
1494
+ maxFileSize = chunkT6PTKWM6_cjs.DEFAULT_MAX_FILE_SIZE,
1216
1495
  maxAttachments = 10,
1217
1496
  onError,
1218
1497
  autoFocus = false,
@@ -1223,11 +1502,11 @@ var MessageComposer = React6__namespace.forwardRef(
1223
1502
  mentionOptions,
1224
1503
  className
1225
1504
  }, ref) => {
1226
- const textareaRef = React6__namespace.useRef(null);
1227
- const [internalContent, setInternalContent] = React6__namespace.useState("");
1505
+ const textareaRef = React__namespace.useRef(null);
1506
+ const [internalContent, setInternalContent] = React__namespace.useState("");
1228
1507
  const isControlled = controlledValue !== void 0;
1229
1508
  const content = isControlled ? controlledValue : internalContent;
1230
- const setContent = React6__namespace.useCallback(
1509
+ const setContent = React__namespace.useCallback(
1231
1510
  (val) => {
1232
1511
  if (isControlled) {
1233
1512
  const newVal = typeof val === "function" ? val(controlledValue) : val;
@@ -1238,20 +1517,20 @@ var MessageComposer = React6__namespace.forwardRef(
1238
1517
  },
1239
1518
  [isControlled, controlledValue, onValueChange]
1240
1519
  );
1241
- const [attachments, setAttachments] = React6__namespace.useState(
1520
+ const [attachments, setAttachments] = React__namespace.useState(
1242
1521
  []
1243
1522
  );
1244
- const [isTyping, setIsTyping] = React6__namespace.useState(false);
1245
- const typingTimeoutRef = React6__namespace.useRef(null);
1246
- React6__namespace.useImperativeHandle(ref, () => textareaRef.current);
1247
- React6__namespace.useEffect(() => {
1523
+ const [isTyping, setIsTyping] = React__namespace.useState(false);
1524
+ const typingTimeoutRef = React__namespace.useRef(null);
1525
+ React__namespace.useImperativeHandle(ref, () => textareaRef.current);
1526
+ React__namespace.useEffect(() => {
1248
1527
  const textarea = textareaRef.current;
1249
1528
  if (textarea) {
1250
1529
  textarea.style.height = "auto";
1251
1530
  textarea.style.height = `${Math.min(textarea.scrollHeight, 150)}px`;
1252
1531
  }
1253
1532
  }, [content]);
1254
- React6__namespace.useEffect(() => {
1533
+ React__namespace.useEffect(() => {
1255
1534
  if (content.length > 0 && !isTyping) {
1256
1535
  setIsTyping(true);
1257
1536
  onTypingStart?.();
@@ -1271,18 +1550,18 @@ var MessageComposer = React6__namespace.forwardRef(
1271
1550
  }
1272
1551
  };
1273
1552
  }, [content, isTyping, onTypingStart, onTypingStop]);
1274
- React6__namespace.useEffect(() => {
1553
+ React__namespace.useEffect(() => {
1275
1554
  if (autoFocus) {
1276
1555
  textareaRef.current?.focus();
1277
1556
  }
1278
1557
  }, [autoFocus]);
1279
1558
  const replyToId = replyTo?.id;
1280
- React6__namespace.useEffect(() => {
1559
+ React__namespace.useEffect(() => {
1281
1560
  if (replyToId !== void 0) {
1282
1561
  textareaRef.current?.focus();
1283
1562
  }
1284
1563
  }, [replyToId]);
1285
- const mention = chunkH3GRO43Z_cjs.useMentionAutocomplete({
1564
+ const mention = chunkT6PTKWM6_cjs.useMentionAutocomplete({
1286
1565
  options: mentionOptions,
1287
1566
  value: content,
1288
1567
  setValue: setContent,
@@ -1324,13 +1603,13 @@ var MessageComposer = React6__namespace.forwardRef(
1324
1603
  onError?.(`Maximum ${maxAttachments} attachments allowed`);
1325
1604
  }
1326
1605
  const newAttachments = filesToAdd.map((file) => {
1327
- const type = chunkH3GRO43Z_cjs.getFileType(file.type);
1606
+ const type = chunkT6PTKWM6_cjs.getFileType(file.type);
1328
1607
  let previewUrl;
1329
1608
  if (type === "image" || type === "video") {
1330
1609
  previewUrl = URL.createObjectURL(file);
1331
1610
  }
1332
1611
  return {
1333
- id: chunkH3GRO43Z_cjs.generateAttachmentId(),
1612
+ id: chunkT6PTKWM6_cjs.generateAttachmentId(),
1334
1613
  file,
1335
1614
  previewUrl,
1336
1615
  type,
@@ -1355,7 +1634,7 @@ var MessageComposer = React6__namespace.forwardRef(
1355
1634
  return prev.filter((a) => a.id !== attachmentId);
1356
1635
  });
1357
1636
  };
1358
- React6__namespace.useEffect(() => {
1637
+ React__namespace.useEffect(() => {
1359
1638
  const currentAttachments = attachments;
1360
1639
  return () => {
1361
1640
  currentAttachments.forEach((a) => {
@@ -1366,7 +1645,7 @@ var MessageComposer = React6__namespace.forwardRef(
1366
1645
  };
1367
1646
  }, [attachments]);
1368
1647
  return /* @__PURE__ */ jsxRuntime.jsx(
1369
- chunkH3GRO43Z_cjs.DragDropZone,
1648
+ chunkT6PTKWM6_cjs.DragDropZone,
1370
1649
  {
1371
1650
  onFilesDropped: handleFilesSelected,
1372
1651
  acceptedTypes: acceptedFileTypes,
@@ -1444,7 +1723,7 @@ var MessageComposer = React6__namespace.forwardRef(
1444
1723
  "border-t border-neutral-200 dark:border-neutral-700"
1445
1724
  ),
1446
1725
  children: attachments.map((attachment) => /* @__PURE__ */ jsxRuntime.jsx(
1447
- chunkH3GRO43Z_cjs.AttachmentPreviewItem,
1726
+ chunkT6PTKWM6_cjs.AttachmentPreviewItem,
1448
1727
  {
1449
1728
  attachment,
1450
1729
  onRemove: () => handleRemoveAttachment(attachment.id)
@@ -1464,7 +1743,7 @@ var MessageComposer = React6__namespace.forwardRef(
1464
1743
  ),
1465
1744
  children: [
1466
1745
  showAttachmentPicker && /* @__PURE__ */ jsxRuntime.jsx(
1467
- chunkH3GRO43Z_cjs.AttachmentPicker,
1746
+ chunkT6PTKWM6_cjs.AttachmentPicker,
1468
1747
  {
1469
1748
  onFilesSelected: handleFilesSelected,
1470
1749
  acceptedTypes: acceptedFileTypes,
@@ -1475,7 +1754,7 @@ var MessageComposer = React6__namespace.forwardRef(
1475
1754
  }
1476
1755
  ),
1477
1756
  showCameraButton && /* @__PURE__ */ jsxRuntime.jsx(
1478
- chunkH3GRO43Z_cjs.CameraButton,
1757
+ chunkT6PTKWM6_cjs.CameraButton,
1479
1758
  {
1480
1759
  onCapture: (file) => handleFilesSelected([file]),
1481
1760
  disabled: disabled || attachments.length >= maxAttachments
@@ -1488,7 +1767,7 @@ var MessageComposer = React6__namespace.forwardRef(
1488
1767
  className: "relative flex-1",
1489
1768
  ref: mention.anchorRef,
1490
1769
  children: [
1491
- /* @__PURE__ */ jsxRuntime.jsx(chunkH3GRO43Z_cjs.MentionMenu, { mention }),
1770
+ /* @__PURE__ */ jsxRuntime.jsx(chunkT6PTKWM6_cjs.MentionMenu, { mention }),
1492
1771
  /* @__PURE__ */ jsxRuntime.jsx(
1493
1772
  "textarea",
1494
1773
  {
@@ -1625,7 +1904,7 @@ function formatLastSeen(date) {
1625
1904
  if (diffDays < 7) return `${diffDays}d ago`;
1626
1905
  return date.toLocaleDateString();
1627
1906
  }
1628
- var ConversationHeader = React6__namespace.forwardRef(
1907
+ var ConversationHeader = React__namespace.forwardRef(
1629
1908
  ({
1630
1909
  className,
1631
1910
  size,
@@ -1747,7 +2026,7 @@ var ConversationHeader = React6__namespace.forwardRef(
1747
2026
  }
1748
2027
  );
1749
2028
  ConversationHeader.displayName = "ConversationHeader";
1750
- var ConversationListItem = React6__namespace.forwardRef(({ className, conversation, isSelected, onSelect, ...props }, ref) => {
2029
+ var ConversationListItem = React__namespace.forwardRef(({ className, conversation, isSelected, onSelect, ...props }, ref) => {
1751
2030
  const participant = conversation.participants.find((p) => !p.isCurrentUser);
1752
2031
  const title = getConversationTitle(conversation, participant);
1753
2032
  const avatarUrl = conversation.avatarUrl || participant?.avatarUrl;
@@ -1955,7 +2234,7 @@ function ConversationListSkeleton({
1955
2234
  }
1956
2235
  ConversationListSkeleton.displayName = "ConversationListSkeleton";
1957
2236
  function LightboxModal({ attachment, onClose }) {
1958
- React6__namespace.useEffect(() => {
2237
+ React__namespace.useEffect(() => {
1959
2238
  const handleKeyDown = (event) => {
1960
2239
  if (event.key === "Escape") {
1961
2240
  onClose();
@@ -2052,7 +2331,7 @@ function LightboxModal({ attachment, onClose }) {
2052
2331
  );
2053
2332
  }
2054
2333
  LightboxModal.displayName = "LightboxModal";
2055
- var MessageThread = React6__namespace.forwardRef(
2334
+ var MessageThread = React__namespace.forwardRef(
2056
2335
  ({
2057
2336
  conversation,
2058
2337
  messages,
@@ -2083,8 +2362,20 @@ var MessageThread = React6__namespace.forwardRef(
2083
2362
  onError,
2084
2363
  className
2085
2364
  }, ref) => {
2086
- const [lightboxAttachment, setLightboxAttachment] = React6__namespace.useState(null);
2087
- const [replyTo, setReplyTo] = React6__namespace.useState(null);
2365
+ const [lightboxAttachment, setLightboxAttachment] = React__namespace.useState(null);
2366
+ const [replyTo, setReplyTo] = React__namespace.useState(null);
2367
+ const composerRef = React__namespace.useRef(null);
2368
+ const [draft, setDraft] = React__namespace.useState("");
2369
+ const draftEpochRef = React__namespace.useRef(0);
2370
+ const handleDraftChange = React__namespace.useCallback((value) => {
2371
+ draftEpochRef.current += 1;
2372
+ setDraft(value);
2373
+ }, []);
2374
+ const { stopTyping } = useTypingEmulation({
2375
+ value: draft,
2376
+ onTypingStart: eventHandlers.onTypingStart,
2377
+ onTypingStop: eventHandlers.onTypingStop
2378
+ });
2088
2379
  const participant = conversation?.type === "direct" ? conversation.participants.find((p) => p.id !== currentUser.id) : void 0;
2089
2380
  const handleAttachmentClick = (attachment, message) => {
2090
2381
  if (attachment.type === "image" || attachment.type === "video") {
@@ -2093,12 +2384,21 @@ var MessageThread = React6__namespace.forwardRef(
2093
2384
  eventHandlers.onAttachmentClick?.(attachment, message);
2094
2385
  };
2095
2386
  const handleSendMessage = async (newMessage) => {
2387
+ stopTyping();
2096
2388
  const messageWithReply = {
2097
2389
  ...newMessage,
2098
2390
  replyToId: replyTo?.id || newMessage.replyToId
2099
2391
  };
2100
2392
  setReplyTo(null);
2101
- await eventHandlers.onSendMessage?.(messageWithReply);
2393
+ const epoch = draftEpochRef.current;
2394
+ try {
2395
+ await Promise.resolve(eventHandlers.onSendMessage?.(messageWithReply));
2396
+ } catch (error) {
2397
+ if (draftEpochRef.current === epoch) {
2398
+ setDraft(newMessage.content);
2399
+ }
2400
+ throw error;
2401
+ }
2102
2402
  };
2103
2403
  return /* @__PURE__ */ jsxRuntime.jsxs(
2104
2404
  "div",
@@ -2122,11 +2422,13 @@ var MessageThread = React6__namespace.forwardRef(
2122
2422
  }
2123
2423
  ),
2124
2424
  /* @__PURE__ */ jsxRuntime.jsx(
2125
- chunkH3GRO43Z_cjs.DragDropZone,
2425
+ chunkT6PTKWM6_cjs.DragDropZone,
2126
2426
  {
2127
- onFilesDropped: () => {
2128
- onError?.("Drop files on the composer to attach them");
2427
+ onFilesDropped: (files) => {
2428
+ composerRef.current?.addFiles(files);
2129
2429
  },
2430
+ maxFiles: maxAttachments,
2431
+ onError,
2130
2432
  disabled: !showAttachmentPicker,
2131
2433
  className: "flex-1 overflow-hidden",
2132
2434
  children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -2152,23 +2454,38 @@ var MessageThread = React6__namespace.forwardRef(
2152
2454
  }
2153
2455
  ),
2154
2456
  /* @__PURE__ */ jsxRuntime.jsx(
2155
- MessageComposer,
2457
+ "div",
2156
2458
  {
2157
- onSend: handleSendMessage,
2158
- onTypingStart: eventHandlers.onTypingStart,
2159
- onTypingStop: eventHandlers.onTypingStop,
2160
- placeholder,
2161
- maxLength: maxMessageLength,
2162
- showCharacterCount,
2163
- isSending,
2164
- showAttachmentPicker,
2165
- showCameraButton,
2166
- acceptedFileTypes,
2167
- maxFileSize,
2168
- maxAttachments,
2169
- onError,
2170
- replyTo,
2171
- onCancelReply: () => setReplyTo(null)
2459
+ "data-slot": "message-thread-composer",
2460
+ className: "shrink-0 border-t border-neutral-200 p-3 dark:border-neutral-700",
2461
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2462
+ chunkT6PTKWM6_cjs.ChatComposer,
2463
+ {
2464
+ ref: composerRef,
2465
+ value: draft,
2466
+ onValueChange: handleDraftChange,
2467
+ onSend: handleSendMessage,
2468
+ placeholder,
2469
+ maxLength: maxMessageLength,
2470
+ showCharacterCount,
2471
+ isSending,
2472
+ allowAttachments: showAttachmentPicker,
2473
+ acceptedFileTypes: acceptedFileTypes ?? chunkT6PTKWM6_cjs.DEFAULT_ACCEPTED_FILE_TYPES,
2474
+ maxFileSize: maxFileSize ?? chunkT6PTKWM6_cjs.DEFAULT_MAX_FILE_SIZE,
2475
+ maxAttachments,
2476
+ onError,
2477
+ replyTo,
2478
+ onCancelReply: () => setReplyTo(null),
2479
+ inputLabel: "Message",
2480
+ micSlot: showCameraButton ? /* @__PURE__ */ jsxRuntime.jsx(
2481
+ chunkT6PTKWM6_cjs.CameraButton,
2482
+ {
2483
+ onCapture: (file) => composerRef.current?.addFiles([file]),
2484
+ disabled: isSending
2485
+ }
2486
+ ) : void 0
2487
+ }
2488
+ )
2172
2489
  }
2173
2490
  ),
2174
2491
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2248,249 +2565,6 @@ function MessagingSplitView({
2248
2565
  );
2249
2566
  }
2250
2567
  MessagingSplitView.displayName = "MessagingSplitView";
2251
- function useMessages(options) {
2252
- const {
2253
- initialMessages = [],
2254
- currentUser,
2255
- onSend,
2256
- onRetry,
2257
- onLoadMore
2258
- } = options;
2259
- const [messages, setMessages] = React6__namespace.useState(initialMessages);
2260
- const [isSending, setIsSending] = React6__namespace.useState(false);
2261
- const [isLoadingMore, setIsLoadingMore] = React6__namespace.useState(false);
2262
- React6__namespace.useEffect(() => {
2263
- setMessages(initialMessages);
2264
- }, [initialMessages]);
2265
- const addMessage = React6__namespace.useCallback((message) => {
2266
- setMessages((prev) => {
2267
- if (prev.some((m) => m.id === message.id)) {
2268
- return prev;
2269
- }
2270
- return [...prev, message];
2271
- });
2272
- }, []);
2273
- const updateMessage = React6__namespace.useCallback(
2274
- (messageId, updates) => {
2275
- setMessages(
2276
- (prev) => prev.map((m) => m.id === messageId ? { ...m, ...updates } : m)
2277
- );
2278
- },
2279
- []
2280
- );
2281
- const removeMessage = React6__namespace.useCallback((messageId) => {
2282
- setMessages((prev) => prev.filter((m) => m.id !== messageId));
2283
- }, []);
2284
- const updateStatus = React6__namespace.useCallback(
2285
- (messageId, status) => {
2286
- updateMessage(messageId, { status });
2287
- },
2288
- [updateMessage]
2289
- );
2290
- const markAsRead = React6__namespace.useCallback(
2291
- (messageId) => {
2292
- updateStatus(messageId, "read");
2293
- },
2294
- [updateStatus]
2295
- );
2296
- const sendMessage = React6__namespace.useCallback(
2297
- async (newMessage) => {
2298
- const optimisticId = `optimistic-${Date.now()}`;
2299
- const optimisticMessage = {
2300
- id: optimisticId,
2301
- type: "text",
2302
- content: newMessage.content,
2303
- sender: currentUser,
2304
- timestamp: /* @__PURE__ */ new Date(),
2305
- status: "sending",
2306
- attachments: []
2307
- // Would handle attachment uploads here
2308
- };
2309
- addMessage(optimisticMessage);
2310
- setIsSending(true);
2311
- try {
2312
- if (onSend) {
2313
- const sentMessage = await onSend(newMessage);
2314
- setMessages(
2315
- (prev) => prev.map((m) => m.id === optimisticId ? sentMessage : m)
2316
- );
2317
- } else {
2318
- updateStatus(optimisticId, "sent");
2319
- }
2320
- } catch {
2321
- updateStatus(optimisticId, "failed");
2322
- } finally {
2323
- setIsSending(false);
2324
- }
2325
- },
2326
- [currentUser, onSend, addMessage, updateStatus]
2327
- );
2328
- const retryMessage = React6__namespace.useCallback(
2329
- async (messageId) => {
2330
- updateStatus(messageId, "sending");
2331
- try {
2332
- if (onRetry) {
2333
- await onRetry(messageId);
2334
- updateStatus(messageId, "sent");
2335
- }
2336
- } catch {
2337
- updateStatus(messageId, "failed");
2338
- }
2339
- },
2340
- [onRetry, updateStatus]
2341
- );
2342
- const loadMore = React6__namespace.useCallback(async () => {
2343
- if (isLoadingMore || !onLoadMore) return;
2344
- setIsLoadingMore(true);
2345
- try {
2346
- const olderMessages = await onLoadMore();
2347
- setMessages((prev) => [...olderMessages, ...prev]);
2348
- } finally {
2349
- setIsLoadingMore(false);
2350
- }
2351
- }, [isLoadingMore, onLoadMore]);
2352
- return {
2353
- messages,
2354
- addMessage,
2355
- updateMessage,
2356
- removeMessage,
2357
- sendMessage,
2358
- retryMessage,
2359
- loadMore,
2360
- isSending,
2361
- isLoadingMore,
2362
- markAsRead,
2363
- updateStatus
2364
- };
2365
- }
2366
- function useTypingIndicator(options = {}) {
2367
- const {
2368
- typingParticipants: initialParticipants = [],
2369
- debounceTime = 2e3,
2370
- onTypingStart,
2371
- onTypingStop
2372
- } = options;
2373
- const [participants, setParticipants] = React6__namespace.useState(initialParticipants);
2374
- const [isLocalTyping, setIsLocalTyping] = React6__namespace.useState(false);
2375
- const typingTimeoutRef = React6__namespace.useRef(null);
2376
- React6__namespace.useEffect(() => {
2377
- setParticipants(initialParticipants);
2378
- }, [initialParticipants]);
2379
- const startTyping = React6__namespace.useCallback(() => {
2380
- if (!isLocalTyping) {
2381
- setIsLocalTyping(true);
2382
- onTypingStart?.();
2383
- }
2384
- if (typingTimeoutRef.current) {
2385
- clearTimeout(typingTimeoutRef.current);
2386
- }
2387
- typingTimeoutRef.current = setTimeout(() => {
2388
- setIsLocalTyping(false);
2389
- onTypingStop?.();
2390
- }, debounceTime);
2391
- }, [isLocalTyping, debounceTime, onTypingStart, onTypingStop]);
2392
- const stopTyping = React6__namespace.useCallback(() => {
2393
- if (typingTimeoutRef.current) {
2394
- clearTimeout(typingTimeoutRef.current);
2395
- }
2396
- setIsLocalTyping(false);
2397
- onTypingStop?.();
2398
- }, [onTypingStop]);
2399
- React6__namespace.useEffect(() => {
2400
- return () => {
2401
- if (typingTimeoutRef.current) {
2402
- clearTimeout(typingTimeoutRef.current);
2403
- }
2404
- };
2405
- }, []);
2406
- const typingState = React6__namespace.useMemo(
2407
- () => ({
2408
- participants,
2409
- lastUpdated: /* @__PURE__ */ new Date()
2410
- }),
2411
- [participants]
2412
- );
2413
- return {
2414
- typingState,
2415
- startTyping,
2416
- stopTyping,
2417
- setTypingParticipants: setParticipants
2418
- };
2419
- }
2420
- function useMessageScroll(options) {
2421
- const { messages, currentUserId, threshold = 100 } = options;
2422
- const scrollContainerRef = React6__namespace.useRef(null);
2423
- const bottomRef = React6__namespace.useRef(null);
2424
- const [isScrolledUp, setIsScrolledUp] = React6__namespace.useState(false);
2425
- const prevMessageCountRef = React6__namespace.useRef(messages.length);
2426
- React6__namespace.useEffect(() => {
2427
- const container = scrollContainerRef.current;
2428
- if (!container) return;
2429
- const handleScroll = () => {
2430
- const { scrollTop, scrollHeight, clientHeight } = container;
2431
- const isAtBottom = scrollHeight - scrollTop - clientHeight < threshold;
2432
- setIsScrolledUp(!isAtBottom);
2433
- };
2434
- container.addEventListener("scroll", handleScroll);
2435
- return () => container.removeEventListener("scroll", handleScroll);
2436
- }, [threshold]);
2437
- const scrollToBottom = React6__namespace.useCallback((smooth = true) => {
2438
- bottomRef.current?.scrollIntoView({
2439
- behavior: smooth ? "smooth" : "auto"
2440
- });
2441
- }, []);
2442
- React6__namespace.useEffect(() => {
2443
- const messageCountChanged = messages.length !== prevMessageCountRef.current;
2444
- prevMessageCountRef.current = messages.length;
2445
- if (!messageCountChanged) return;
2446
- const lastMessage = messages[messages.length - 1];
2447
- const isOutgoing = lastMessage?.sender.id === currentUserId;
2448
- if (isOutgoing || !isScrolledUp) {
2449
- scrollToBottom(true);
2450
- }
2451
- }, [messages, currentUserId, isScrolledUp, scrollToBottom]);
2452
- return {
2453
- scrollContainerRef,
2454
- bottomRef,
2455
- isScrolledUp,
2456
- scrollToBottom
2457
- };
2458
- }
2459
- function useReadReceipts(options) {
2460
- const { currentUserId, onMarkRead, threshold = 0.5 } = options;
2461
- const observerRef = React6__namespace.useRef(null);
2462
- const observedMessagesRef = React6__namespace.useRef(/* @__PURE__ */ new Set());
2463
- React6__namespace.useEffect(() => {
2464
- observerRef.current = new IntersectionObserver(
2465
- (entries) => {
2466
- entries.forEach((entry) => {
2467
- if (entry.isIntersecting) {
2468
- const messageId = entry.target.getAttribute("data-message-id");
2469
- if (messageId && !observedMessagesRef.current.has(messageId)) {
2470
- observedMessagesRef.current.add(messageId);
2471
- onMarkRead?.(messageId);
2472
- }
2473
- }
2474
- });
2475
- },
2476
- { threshold }
2477
- );
2478
- return () => {
2479
- observerRef.current?.disconnect();
2480
- };
2481
- }, [onMarkRead, threshold]);
2482
- const observeMessage = React6__namespace.useCallback(
2483
- (element, message) => {
2484
- if (!element || !observerRef.current) return;
2485
- if (message.sender.id !== currentUserId && message.status !== "read" && !observedMessagesRef.current.has(message.id)) {
2486
- element.setAttribute("data-message-id", message.id);
2487
- observerRef.current.observe(element);
2488
- }
2489
- },
2490
- [currentUserId]
2491
- );
2492
- return { observeMessage };
2493
- }
2494
2568
 
2495
2569
  exports.AttachmentPreview = AttachmentPreview;
2496
2570
  exports.CharacterCounter = CharacterCounter;
@@ -2524,6 +2598,7 @@ exports.sendButtonVariants = sendButtonVariants;
2524
2598
  exports.useMessageScroll = useMessageScroll;
2525
2599
  exports.useMessages = useMessages;
2526
2600
  exports.useReadReceipts = useReadReceipts;
2601
+ exports.useTypingEmulation = useTypingEmulation;
2527
2602
  exports.useTypingIndicator = useTypingIndicator;
2528
- //# sourceMappingURL=chunk-ZXRVVNT5.cjs.map
2529
- //# sourceMappingURL=chunk-ZXRVVNT5.cjs.map
2603
+ //# sourceMappingURL=chunk-XCNDZNY7.cjs.map
2604
+ //# sourceMappingURL=chunk-XCNDZNY7.cjs.map