hermes-chat-react 0.1.2 → 0.1.3

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.
package/dist/react.cjs CHANGED
@@ -30,20 +30,53 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/react.ts
31
31
  var react_exports = {};
32
32
  __export(react_exports, {
33
+ Avatar: () => Avatar,
34
+ Chat: () => Chat,
35
+ ChatContext: () => ChatContext,
33
36
  ChatInput: () => ChatInput,
37
+ ChatProvider: () => ChatProvider,
38
+ ComponentContext: () => ComponentContext,
39
+ ComponentProvider: () => ComponentProvider,
40
+ DateSeparator: () => DateSeparator,
41
+ EmptyStateIndicator: () => EmptyStateIndicator,
34
42
  HermesClient: () => HermesClient,
43
+ LoadingErrorIndicator: () => LoadingErrorIndicator,
44
+ LoadingIndicator: () => LoadingIndicator,
35
45
  MediaMessage: () => MediaMessage,
46
+ Message: () => Message,
47
+ MessageActions: () => MessageActions,
48
+ MessageContext: () => MessageContext,
36
49
  MessageList: () => MessageList,
50
+ MessageProvider: () => MessageProvider,
51
+ MessageStatus: () => MessageStatus,
52
+ Modal: () => Modal,
37
53
  OnlineBadge: () => OnlineBadge,
38
54
  ReactionPicker: () => ReactionPicker,
55
+ Room: () => Room,
56
+ RoomActionContext: () => RoomActionContext,
57
+ RoomActionProvider: () => RoomActionProvider,
39
58
  RoomList: () => RoomList,
40
- TypingIndicator: () => TypingIndicator2,
59
+ RoomStateContext: () => RoomStateContext,
60
+ RoomStateProvider: () => RoomStateProvider,
61
+ Search: () => Search,
62
+ Thread: () => Thread,
63
+ ThreadHeader: () => ThreadHeader,
64
+ TypingContext: () => TypingContext,
65
+ TypingIndicator: () => TypingIndicator,
66
+ TypingProvider: () => TypingProvider,
67
+ Window: () => Window,
68
+ useChatContext: () => useChatContext,
69
+ useComponentContext: () => useComponentContext,
70
+ useMessageContext: () => useMessageContext,
41
71
  useMessages: () => useMessages,
42
72
  usePresence: () => usePresence,
43
73
  useReactions: () => useReactions,
44
74
  useReadReceipts: () => useReadReceipts,
75
+ useRoomActionContext: () => useRoomActionContext,
76
+ useRoomStateContext: () => useRoomStateContext,
45
77
  useRooms: () => useRooms,
46
78
  useTyping: () => useTyping,
79
+ useTypingContext: () => useTypingContext,
47
80
  useUpload: () => useUpload
48
81
  });
49
82
  module.exports = __toCommonJS(react_exports);
@@ -124,10 +157,7 @@ var HermesClient = class extends EventEmitter {
124
157
  body: JSON.stringify({
125
158
  apiKey: cfg.apiKey,
126
159
  secret: cfg.secret,
127
- userId: cfg.userId,
128
- displayName: cfg.displayName ?? cfg.userId,
129
- avatar: cfg.avatar,
130
- email: cfg.email
160
+ userId: cfg.userId
131
161
  })
132
162
  });
133
163
  const data = await res.json();
@@ -278,17 +308,128 @@ var HermesClient = class extends EventEmitter {
278
308
  }
279
309
  };
280
310
 
281
- // src/react/hooks/useMessages.ts
311
+ // src/react/context/ChatContext.tsx
282
312
  var import_react = require("react");
313
+ var import_jsx_runtime = require("react/jsx-runtime");
314
+ var ChatContext = (0, import_react.createContext)(
315
+ void 0
316
+ );
317
+ var ChatProvider = ({
318
+ children,
319
+ value
320
+ }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatContext.Provider, { value, children });
321
+ var useChatContext = (componentName) => {
322
+ const contextValue = (0, import_react.useContext)(ChatContext);
323
+ if (!contextValue) {
324
+ console.warn(
325
+ `useChatContext was called outside of ChatProvider. Make sure this hook is called within a child of the <Chat> component.${componentName ? ` Errored in: ${componentName}` : ""}`
326
+ );
327
+ return {};
328
+ }
329
+ return contextValue;
330
+ };
331
+
332
+ // src/react/context/RoomStateContext.tsx
333
+ var import_react2 = require("react");
334
+ var import_jsx_runtime2 = require("react/jsx-runtime");
335
+ var RoomStateContext = (0, import_react2.createContext)(void 0);
336
+ var RoomStateProvider = ({
337
+ children,
338
+ value
339
+ }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RoomStateContext.Provider, { value, children });
340
+ var useRoomStateContext = (componentName) => {
341
+ const contextValue = (0, import_react2.useContext)(RoomStateContext);
342
+ if (!contextValue) {
343
+ console.warn(
344
+ `useRoomStateContext was called outside of RoomStateProvider. Make sure this hook is called within a child of the <Room> component.${componentName ? ` Errored in: ${componentName}` : ""}`
345
+ );
346
+ return {};
347
+ }
348
+ return contextValue;
349
+ };
350
+
351
+ // src/react/context/RoomActionContext.tsx
352
+ var import_react3 = require("react");
353
+ var import_jsx_runtime3 = require("react/jsx-runtime");
354
+ var RoomActionContext = (0, import_react3.createContext)(void 0);
355
+ var RoomActionProvider = ({
356
+ children,
357
+ value
358
+ }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RoomActionContext.Provider, { value, children });
359
+ var useRoomActionContext = (componentName) => {
360
+ const contextValue = (0, import_react3.useContext)(RoomActionContext);
361
+ if (!contextValue) {
362
+ console.warn(
363
+ `useRoomActionContext was called outside of RoomActionProvider. Make sure this hook is called within a child of the <Room> component.${componentName ? ` Errored in: ${componentName}` : ""}`
364
+ );
365
+ return {};
366
+ }
367
+ return contextValue;
368
+ };
369
+
370
+ // src/react/context/MessageContext.tsx
371
+ var import_react4 = require("react");
372
+ var import_jsx_runtime4 = require("react/jsx-runtime");
373
+ var MessageContext = (0, import_react4.createContext)(
374
+ void 0
375
+ );
376
+ var MessageProvider = ({
377
+ children,
378
+ value
379
+ }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(MessageContext.Provider, { value, children });
380
+ var useMessageContext = (componentName) => {
381
+ const contextValue = (0, import_react4.useContext)(MessageContext);
382
+ if (!contextValue) {
383
+ console.warn(
384
+ `useMessageContext was called outside of MessageProvider.${componentName ? ` Errored in: ${componentName}` : ""}`
385
+ );
386
+ return {};
387
+ }
388
+ return contextValue;
389
+ };
390
+
391
+ // src/react/context/ComponentContext.tsx
392
+ var import_react5 = require("react");
393
+ var import_jsx_runtime5 = require("react/jsx-runtime");
394
+ var ComponentContext = (0, import_react5.createContext)({});
395
+ var ComponentProvider = ({
396
+ children,
397
+ value
398
+ }) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ComponentContext.Provider, { value, children });
399
+ var useComponentContext = (_componentName) => (0, import_react5.useContext)(ComponentContext);
400
+
401
+ // src/react/context/TypingContext.tsx
402
+ var import_react6 = require("react");
403
+ var import_jsx_runtime6 = require("react/jsx-runtime");
404
+ var TypingContext = (0, import_react6.createContext)(
405
+ void 0
406
+ );
407
+ var TypingProvider = ({
408
+ children,
409
+ value
410
+ }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(TypingContext.Provider, { value, children });
411
+ var useTypingContext = (componentName) => {
412
+ const contextValue = (0, import_react6.useContext)(TypingContext);
413
+ if (!contextValue) {
414
+ console.warn(
415
+ `useTypingContext was called outside of TypingProvider.${componentName ? ` Errored in: ${componentName}` : ""}`
416
+ );
417
+ return {};
418
+ }
419
+ return contextValue;
420
+ };
421
+
422
+ // src/react/hooks/useMessages.ts
423
+ var import_react7 = require("react");
283
424
  var useMessages = (client, roomId) => {
284
- const [messages, setMessages] = (0, import_react.useState)([]);
285
- const [loading, setLoading] = (0, import_react.useState)(false);
286
- const [loadingMore, setLoadingMore] = (0, import_react.useState)(false);
287
- const [hasMore, setHasMore] = (0, import_react.useState)(false);
288
- const [error, setError] = (0, import_react.useState)(null);
289
- const [typingUsers, setTypingUsers] = (0, import_react.useState)([]);
290
- const oldestMessageId = (0, import_react.useRef)(void 0);
291
- (0, import_react.useEffect)(() => {
425
+ const [messages, setMessages] = (0, import_react7.useState)([]);
426
+ const [loading, setLoading] = (0, import_react7.useState)(false);
427
+ const [loadingMore, setLoadingMore] = (0, import_react7.useState)(false);
428
+ const [hasMore, setHasMore] = (0, import_react7.useState)(false);
429
+ const [error, setError] = (0, import_react7.useState)(null);
430
+ const [typingUsers, setTypingUsers] = (0, import_react7.useState)([]);
431
+ const oldestMessageId = (0, import_react7.useRef)(void 0);
432
+ (0, import_react7.useEffect)(() => {
292
433
  if (!roomId || !client.isConnected) return;
293
434
  setMessages([]);
294
435
  setHasMore(false);
@@ -301,7 +442,7 @@ var useMessages = (client, roomId) => {
301
442
  if (msgs.length > 0) oldestMessageId.current = msgs[0]._id;
302
443
  }).catch((err) => setError(err.message)).finally(() => setLoading(false));
303
444
  }, [roomId, client.isConnected]);
304
- (0, import_react.useEffect)(() => {
445
+ (0, import_react7.useEffect)(() => {
305
446
  if (!roomId) return;
306
447
  const onReceive = (msg) => {
307
448
  if (msg.roomId !== roomId) return;
@@ -331,7 +472,7 @@ var useMessages = (client, roomId) => {
331
472
  client.off("message:edited", onEdited);
332
473
  };
333
474
  }, [roomId, client]);
334
- (0, import_react.useEffect)(() => {
475
+ (0, import_react7.useEffect)(() => {
335
476
  const onReaction = ({ messageId, reactions }) => {
336
477
  setMessages(
337
478
  (prev) => prev.map((m) => m._id === messageId ? { ...m, reactions } : m)
@@ -342,7 +483,7 @@ var useMessages = (client, roomId) => {
342
483
  client.off("reaction:updated", onReaction);
343
484
  };
344
485
  }, [client]);
345
- (0, import_react.useEffect)(() => {
486
+ (0, import_react7.useEffect)(() => {
346
487
  if (!roomId) return;
347
488
  const onStarted = ({ userId, displayName, roomId: rid }) => {
348
489
  if (rid !== roomId) return;
@@ -363,7 +504,7 @@ var useMessages = (client, roomId) => {
363
504
  setTypingUsers([]);
364
505
  };
365
506
  }, [roomId, client]);
366
- const loadMore = (0, import_react.useCallback)(async () => {
507
+ const loadMore = (0, import_react7.useCallback)(async () => {
367
508
  if (!roomId || loadingMore || !hasMore) return;
368
509
  setLoadingMore(true);
369
510
  try {
@@ -380,28 +521,28 @@ var useMessages = (client, roomId) => {
380
521
  setLoadingMore(false);
381
522
  }
382
523
  }, [roomId, loadingMore, hasMore, client]);
383
- const sendMessage = (0, import_react.useCallback)(
524
+ const sendMessage = (0, import_react7.useCallback)(
384
525
  async (input) => {
385
526
  if (!roomId) throw new Error("No room selected");
386
527
  return client.sendMessage({ ...input, roomId });
387
528
  },
388
529
  [roomId, client]
389
530
  );
390
- const editMessage = (0, import_react.useCallback)(
531
+ const editMessage = (0, import_react7.useCallback)(
391
532
  async (messageId, text) => {
392
533
  if (!roomId) throw new Error("No room selected");
393
534
  return client.editMessage(messageId, roomId, text);
394
535
  },
395
536
  [roomId, client]
396
537
  );
397
- const deleteMessage = (0, import_react.useCallback)(
538
+ const deleteMessage = (0, import_react7.useCallback)(
398
539
  async (messageId) => {
399
540
  if (!roomId) throw new Error("No room selected");
400
541
  return client.deleteMessage(messageId, roomId);
401
542
  },
402
543
  [roomId, client]
403
544
  );
404
- const addReaction = (0, import_react.useCallback)(
545
+ const addReaction = (0, import_react7.useCallback)(
405
546
  async (messageId, emoji) => {
406
547
  if (!roomId) throw new Error("No room selected");
407
548
  return client.addReaction(messageId, roomId, emoji);
@@ -424,13 +565,13 @@ var useMessages = (client, roomId) => {
424
565
  };
425
566
 
426
567
  // src/react/hooks/useRooms.ts
427
- var import_react2 = require("react");
568
+ var import_react8 = require("react");
428
569
  var useRooms = (client) => {
429
- const [rooms, setRooms] = (0, import_react2.useState)([]);
430
- const [loading, setLoading] = (0, import_react2.useState)(true);
431
- const [error, setError] = (0, import_react2.useState)(null);
432
- const fetchedRef = (0, import_react2.useRef)(false);
433
- const fetchRooms = (0, import_react2.useCallback)(async () => {
570
+ const [rooms, setRooms] = (0, import_react8.useState)([]);
571
+ const [loading, setLoading] = (0, import_react8.useState)(true);
572
+ const [error, setError] = (0, import_react8.useState)(null);
573
+ const fetchedRef = (0, import_react8.useRef)(false);
574
+ const fetchRooms = (0, import_react8.useCallback)(async () => {
434
575
  setLoading(true);
435
576
  setError(null);
436
577
  await new Promise((resolve, reject) => {
@@ -459,7 +600,7 @@ var useRooms = (client) => {
459
600
  setLoading(false);
460
601
  }
461
602
  }, [client]);
462
- (0, import_react2.useEffect)(() => {
603
+ (0, import_react8.useEffect)(() => {
463
604
  fetchRooms();
464
605
  const onConnected = () => {
465
606
  if (!fetchedRef.current) fetchRooms();
@@ -469,7 +610,7 @@ var useRooms = (client) => {
469
610
  client.off("connected", onConnected);
470
611
  };
471
612
  }, [fetchRooms, client]);
472
- (0, import_react2.useEffect)(() => {
613
+ (0, import_react8.useEffect)(() => {
473
614
  const onCreated = (room) => {
474
615
  setRooms((prev) => {
475
616
  if (prev.find((r) => r._id === room._id)) return prev;
@@ -516,7 +657,7 @@ var useRooms = (client) => {
516
657
  client.off("message:receive", onMessage);
517
658
  };
518
659
  }, [client]);
519
- const createDirect = (0, import_react2.useCallback)(
660
+ const createDirect = (0, import_react8.useCallback)(
520
661
  async (input) => {
521
662
  const room = await client.createDirectRoom(input);
522
663
  setRooms((prev) => {
@@ -527,7 +668,7 @@ var useRooms = (client) => {
527
668
  },
528
669
  [client]
529
670
  );
530
- const createGroup = (0, import_react2.useCallback)(
671
+ const createGroup = (0, import_react8.useCallback)(
531
672
  async (input) => {
532
673
  const room = await client.createGroupRoom(input);
533
674
  setRooms((prev) => {
@@ -538,18 +679,18 @@ var useRooms = (client) => {
538
679
  },
539
680
  [client]
540
681
  );
541
- const deleteRoom = (0, import_react2.useCallback)(
682
+ const deleteRoom = (0, import_react8.useCallback)(
542
683
  async (roomId) => {
543
684
  await client.deleteRoom(roomId);
544
685
  setRooms((prev) => prev.filter((r) => r._id !== roomId));
545
686
  },
546
687
  [client]
547
688
  );
548
- const addMember = (0, import_react2.useCallback)(
689
+ const addMember = (0, import_react8.useCallback)(
549
690
  (roomId, userId) => client.addMember(roomId, userId),
550
691
  [client]
551
692
  );
552
- const removeMember = (0, import_react2.useCallback)(
693
+ const removeMember = (0, import_react8.useCallback)(
553
694
  (roomId, userId) => client.removeMember(roomId, userId),
554
695
  [client]
555
696
  );
@@ -567,10 +708,10 @@ var useRooms = (client) => {
567
708
  };
568
709
 
569
710
  // src/react/hooks/usePresence.ts
570
- var import_react3 = require("react");
711
+ var import_react9 = require("react");
571
712
  var usePresence = (client) => {
572
- const [onlineMap, setOnlineMap] = (0, import_react3.useState)(/* @__PURE__ */ new Map());
573
- (0, import_react3.useEffect)(() => {
713
+ const [onlineMap, setOnlineMap] = (0, import_react9.useState)(/* @__PURE__ */ new Map());
714
+ (0, import_react9.useEffect)(() => {
574
715
  const onOnline = ({ userId }) => {
575
716
  setOnlineMap((prev) => new Map(prev).set(userId, true));
576
717
  };
@@ -584,7 +725,7 @@ var usePresence = (client) => {
584
725
  client.off("user:offline", onOffline);
585
726
  };
586
727
  }, [client]);
587
- const isOnline = (0, import_react3.useCallback)(
728
+ const isOnline = (0, import_react9.useCallback)(
588
729
  (userId) => onlineMap.get(userId) ?? false,
589
730
  [onlineMap]
590
731
  );
@@ -593,17 +734,17 @@ var usePresence = (client) => {
593
734
  };
594
735
 
595
736
  // src/react/hooks/useTyping.ts
596
- var import_react4 = require("react");
737
+ var import_react10 = require("react");
597
738
  var useTyping = (client, roomId) => {
598
- const [typingUsers, setTypingUsers] = (0, import_react4.useState)(
739
+ const [typingUsers, setTypingUsers] = (0, import_react10.useState)(
599
740
  /* @__PURE__ */ new Map()
600
741
  );
601
- const timeouts = (0, import_react4.useRef)(
742
+ const timeouts = (0, import_react10.useRef)(
602
743
  /* @__PURE__ */ new Map()
603
744
  );
604
- const typingRef = (0, import_react4.useRef)(false);
605
- const stopTimeout = (0, import_react4.useRef)(null);
606
- (0, import_react4.useEffect)(() => {
745
+ const typingRef = (0, import_react10.useRef)(false);
746
+ const stopTimeout = (0, import_react10.useRef)(null);
747
+ (0, import_react10.useEffect)(() => {
607
748
  if (!roomId) return;
608
749
  const onStart = (event) => {
609
750
  if (event.roomId !== roomId) return;
@@ -642,7 +783,7 @@ var useTyping = (client, roomId) => {
642
783
  timeouts.current.clear();
643
784
  };
644
785
  }, [roomId, client]);
645
- const startTyping = (0, import_react4.useCallback)(() => {
786
+ const startTyping = (0, import_react10.useCallback)(() => {
646
787
  if (!roomId) return;
647
788
  if (!typingRef.current) {
648
789
  client.startTyping(roomId);
@@ -654,7 +795,7 @@ var useTyping = (client, roomId) => {
654
795
  typingRef.current = false;
655
796
  }, 3e3);
656
797
  }, [roomId, client]);
657
- const stopTyping = (0, import_react4.useCallback)(() => {
798
+ const stopTyping = (0, import_react10.useCallback)(() => {
658
799
  if (!roomId) return;
659
800
  if (stopTimeout.current) clearTimeout(stopTimeout.current);
660
801
  if (typingRef.current) {
@@ -679,10 +820,10 @@ var useTyping = (client, roomId) => {
679
820
  };
680
821
 
681
822
  // src/react/hooks/useReadReceipts.ts
682
- var import_react5 = require("react");
823
+ var import_react11 = require("react");
683
824
  var useReadReceipts = (client, roomId) => {
684
- const [receipts, setReceipts] = (0, import_react5.useState)(/* @__PURE__ */ new Map());
685
- (0, import_react5.useEffect)(() => {
825
+ const [receipts, setReceipts] = (0, import_react11.useState)(/* @__PURE__ */ new Map());
826
+ (0, import_react11.useEffect)(() => {
686
827
  if (!roomId) return;
687
828
  const onReceipt = (event) => {
688
829
  if (event.roomId !== roomId) return;
@@ -697,14 +838,14 @@ var useReadReceipts = (client, roomId) => {
697
838
  client.on("receipt:updated", onReceipt);
698
839
  return () => client.off("receipt:updated", onReceipt);
699
840
  }, [roomId, client]);
700
- const markSeen = (0, import_react5.useCallback)(
841
+ const markSeen = (0, import_react11.useCallback)(
701
842
  async (lastMessageId) => {
702
843
  if (!roomId) return;
703
844
  await client.markSeen(roomId, lastMessageId);
704
845
  },
705
846
  [roomId, client]
706
847
  );
707
- const seenBy = (0, import_react5.useCallback)(
848
+ const seenBy = (0, import_react11.useCallback)(
708
849
  (messageId) => {
709
850
  return Array.from(receipts.get(messageId) ?? []);
710
851
  },
@@ -714,16 +855,16 @@ var useReadReceipts = (client, roomId) => {
714
855
  };
715
856
 
716
857
  // src/react/hooks/useReactions.ts
717
- var import_react6 = require("react");
858
+ var import_react12 = require("react");
718
859
  var useReactions = (client, roomId) => {
719
- const react = (0, import_react6.useCallback)(
860
+ const react = (0, import_react12.useCallback)(
720
861
  async (messageId, emoji) => {
721
862
  if (!roomId) throw new Error("No room selected");
722
863
  await client.addReaction(messageId, roomId, emoji);
723
864
  },
724
865
  [roomId, client]
725
866
  );
726
- const hasReacted = (0, import_react6.useCallback)(
867
+ const hasReacted = (0, import_react12.useCallback)(
727
868
  (reactions, emoji) => {
728
869
  const userId = client.currentUser?.userId;
729
870
  if (!userId) return false;
@@ -731,25 +872,25 @@ var useReactions = (client, roomId) => {
731
872
  },
732
873
  [client]
733
874
  );
734
- const getCount = (0, import_react6.useCallback)(
875
+ const getCount = (0, import_react12.useCallback)(
735
876
  (reactions, emoji) => {
736
877
  return reactions.find((r) => r.emoji === emoji)?.users.length ?? 0;
737
878
  },
738
879
  []
739
880
  );
740
- const getEmojis = (0, import_react6.useCallback)((reactions) => {
881
+ const getEmojis = (0, import_react12.useCallback)((reactions) => {
741
882
  return reactions.filter((r) => r.users.length > 0).map((r) => r.emoji);
742
883
  }, []);
743
884
  return { react, hasReacted, getCount, getEmojis };
744
885
  };
745
886
 
746
887
  // src/react/hooks/useUpload.ts
747
- var import_react7 = require("react");
888
+ var import_react13 = require("react");
748
889
  var useUpload = (client) => {
749
- const [uploading, setUploading] = (0, import_react7.useState)(false);
750
- const [error, setError] = (0, import_react7.useState)(null);
751
- const [lastUpload, setLastUpload] = (0, import_react7.useState)(null);
752
- const upload = (0, import_react7.useCallback)(
890
+ const [uploading, setUploading] = (0, import_react13.useState)(false);
891
+ const [error, setError] = (0, import_react13.useState)(null);
892
+ const [lastUpload, setLastUpload] = (0, import_react13.useState)(null);
893
+ const upload = (0, import_react13.useCallback)(
753
894
  async (file) => {
754
895
  setUploading(true);
755
896
  setError(null);
@@ -766,7 +907,7 @@ var useUpload = (client) => {
766
907
  },
767
908
  [client]
768
909
  );
769
- const sendFile = (0, import_react7.useCallback)(
910
+ const sendFile = (0, import_react13.useCallback)(
770
911
  async (roomId, file, replyTo) => {
771
912
  setUploading(true);
772
913
  setError(null);
@@ -793,7 +934,7 @@ var useUpload = (client) => {
793
934
  },
794
935
  [client]
795
936
  );
796
- const validate = (0, import_react7.useCallback)((file, maxMb = 50) => {
937
+ const validate = (0, import_react13.useCallback)((file, maxMb = 50) => {
797
938
  if (file.size > maxMb * 1024 * 1024) {
798
939
  return `File too large. Max size is ${maxMb}MB.`;
799
940
  }
@@ -820,151 +961,616 @@ var useUpload = (client) => {
820
961
  return { upload, sendFile, validate, uploading, error, lastUpload };
821
962
  };
822
963
 
823
- // src/react/components/MessageList.tsx
824
- var import_react8 = require("react");
825
- var import_jsx_runtime = require("react/jsx-runtime");
826
- var formatTime = (iso) => new Date(iso).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
827
- var formatFileSize = (bytes) => {
828
- if (!bytes) return "";
829
- if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
830
- if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;
831
- return `${bytes} B`;
964
+ // src/react/components/Chat/Chat.tsx
965
+ var import_react14 = require("react");
966
+ var import_jsx_runtime7 = require("react/jsx-runtime");
967
+ var Chat = ({
968
+ client,
969
+ theme = "light",
970
+ customClasses,
971
+ initialNavOpen = false,
972
+ children
973
+ }) => {
974
+ const [activeRoom, setActiveRoom] = (0, import_react14.useState)(void 0);
975
+ const [navOpen, setNavOpen] = (0, import_react14.useState)(initialNavOpen);
976
+ const [currentUser, setCurrentUser] = (0, import_react14.useState)(
977
+ client.currentUser
978
+ );
979
+ (0, import_react14.useEffect)(() => {
980
+ if (client.currentUser) {
981
+ setCurrentUser(client.currentUser);
982
+ }
983
+ const onConnected = () => setCurrentUser(client.currentUser);
984
+ client.on("connected", onConnected);
985
+ return () => {
986
+ client.off("connected", onConnected);
987
+ };
988
+ }, [client]);
989
+ const openMobileNav = (0, import_react14.useCallback)(() => setNavOpen(true), []);
990
+ const closeMobileNav = (0, import_react14.useCallback)(() => setNavOpen(false), []);
991
+ const handleSetActiveRoom = (0, import_react14.useCallback)(
992
+ (room) => {
993
+ setActiveRoom(room);
994
+ setNavOpen(false);
995
+ },
996
+ []
997
+ );
998
+ const chatContextValue = (0, import_react14.useMemo)(
999
+ () => ({
1000
+ client,
1001
+ currentUser,
1002
+ theme,
1003
+ activeRoom,
1004
+ setActiveRoom: handleSetActiveRoom,
1005
+ openMobileNav,
1006
+ closeMobileNav,
1007
+ navOpen,
1008
+ customClasses
1009
+ }),
1010
+ [
1011
+ client,
1012
+ currentUser,
1013
+ theme,
1014
+ activeRoom,
1015
+ handleSetActiveRoom,
1016
+ openMobileNav,
1017
+ closeMobileNav,
1018
+ navOpen,
1019
+ customClasses
1020
+ ]
1021
+ );
1022
+ const containerClass = customClasses?.chat || `hermes-chat hermes-chat--${theme}`;
1023
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ChatProvider, { value: chatContextValue, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: containerClass, children }) });
832
1024
  };
833
- var REACTION_EMOJIS = [
834
- "\u{1FAE0}",
835
- "\u{1F979}",
836
- "\u{1FAE1}",
837
- "\u{1F90C}",
838
- "\u{1FAF6}",
839
- "\u{1F480}",
840
- "\u{1F525}",
841
- "\u2728",
842
- "\u{1FAE3}",
843
- "\u{1F62E}\u200D\u{1F4A8}",
844
- "\u{1FA84}",
845
- "\u{1F972}",
846
- "\u{1F485}",
847
- "\u{1FAE6}",
848
- "\u{1F92F}",
849
- "\u{1F31A}",
850
- "\u{1F441}\uFE0F",
851
- "\u{1FAC0}",
852
- "\u{1F98B}",
853
- "\u{1FA90}"
854
- ];
855
- var EmojiPicker = ({ onPick, onClose, isOwn }) => {
856
- const ref = (0, import_react8.useRef)(null);
857
- (0, import_react8.useEffect)(() => {
858
- const handler = (e) => {
859
- if (ref.current && !ref.current.contains(e.target)) onClose();
1025
+
1026
+ // src/react/components/Room/Room.tsx
1027
+ var import_react15 = require("react");
1028
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1029
+ var Room = ({
1030
+ roomId,
1031
+ children,
1032
+ // Component overrides forwarded to ComponentContext
1033
+ Avatar: Avatar2,
1034
+ Message: MessageOverride,
1035
+ MessageStatus: MessageStatus2,
1036
+ MessageActions: MessageActions2,
1037
+ DateSeparator: DateSeparator2,
1038
+ EmptyStateIndicator: EmptyStateIndicator2,
1039
+ LoadingIndicator: LoadingIndicator2,
1040
+ LoadingErrorIndicator: LoadingErrorIndicator2,
1041
+ ReactionPicker: ReactionPicker2,
1042
+ TypingIndicator: TypingIndicator2,
1043
+ MediaMessage: MediaMessage2,
1044
+ ThreadHeader: ThreadHeader2,
1045
+ Modal: Modal2,
1046
+ ChatInput: ChatInput2,
1047
+ RoomListItem,
1048
+ Search: Search2,
1049
+ OnlineBadge: OnlineBadge2
1050
+ }) => {
1051
+ const { client, customClasses } = useChatContext("Room");
1052
+ const [messages, setMessages] = (0, import_react15.useState)([]);
1053
+ const [loading, setLoading] = (0, import_react15.useState)(true);
1054
+ const [loadingMore, setLoadingMore] = (0, import_react15.useState)(false);
1055
+ const [hasMore, setHasMore] = (0, import_react15.useState)(false);
1056
+ const [error, setError] = (0, import_react15.useState)(null);
1057
+ const oldestMessageId = (0, import_react15.useRef)(void 0);
1058
+ const [thread, setThread] = (0, import_react15.useState)(null);
1059
+ const [threadMessages, setThreadMessages] = (0, import_react15.useState)([]);
1060
+ const [threadHasMore, setThreadHasMore] = (0, import_react15.useState)(false);
1061
+ const [threadLoadingMore, setThreadLoadingMore] = (0, import_react15.useState)(false);
1062
+ const [typingUsers, setTypingUsers] = (0, import_react15.useState)(
1063
+ /* @__PURE__ */ new Map()
1064
+ );
1065
+ const typingTimeouts = (0, import_react15.useRef)(
1066
+ /* @__PURE__ */ new Map()
1067
+ );
1068
+ const isTypingRef = (0, import_react15.useRef)(false);
1069
+ const stopTypingTimeout = (0, import_react15.useRef)(null);
1070
+ (0, import_react15.useEffect)(() => {
1071
+ if (!roomId || !client?.isConnected) return;
1072
+ setMessages([]);
1073
+ setHasMore(false);
1074
+ setThread(null);
1075
+ setThreadMessages([]);
1076
+ oldestMessageId.current = void 0;
1077
+ setLoading(true);
1078
+ setError(null);
1079
+ client.getHistory(roomId).then(({ messages: msgs, hasMore: more }) => {
1080
+ setMessages(msgs);
1081
+ setHasMore(more);
1082
+ if (msgs.length > 0) oldestMessageId.current = msgs[0]._id;
1083
+ }).catch((err) => setError(err.message)).finally(() => setLoading(false));
1084
+ }, [roomId, client?.isConnected]);
1085
+ (0, import_react15.useEffect)(() => {
1086
+ if (!roomId || !client) return;
1087
+ const onReceive = (msg) => {
1088
+ if (msg.roomId !== roomId) return;
1089
+ if (msg.threadParentId && thread && msg.threadParentId === thread._id) {
1090
+ setThreadMessages(
1091
+ (prev) => prev.find((m) => m._id === msg._id) ? prev : [...prev, msg]
1092
+ );
1093
+ setMessages(
1094
+ (prev) => prev.map(
1095
+ (m) => m._id === msg.threadParentId ? { ...m, replyCount: (m.replyCount || 0) + 1 } : m
1096
+ )
1097
+ );
1098
+ return;
1099
+ }
1100
+ setMessages(
1101
+ (prev) => prev.find((m) => m._id === msg._id) ? prev : [...prev, msg]
1102
+ );
860
1103
  };
861
- document.addEventListener("mousedown", handler);
862
- return () => document.removeEventListener("mousedown", handler);
863
- }, [onClose]);
864
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1104
+ const onDeleted = ({ messageId }) => {
1105
+ setMessages(
1106
+ (prev) => prev.map(
1107
+ (m) => m._id === messageId ? { ...m, isDeleted: true, text: void 0 } : m
1108
+ )
1109
+ );
1110
+ setThreadMessages(
1111
+ (prev) => prev.map(
1112
+ (m) => m._id === messageId ? { ...m, isDeleted: true, text: void 0 } : m
1113
+ )
1114
+ );
1115
+ };
1116
+ const onEdited = (msg) => {
1117
+ setMessages((prev) => prev.map((m) => m._id === msg._id ? msg : m));
1118
+ setThreadMessages(
1119
+ (prev) => prev.map((m) => m._id === msg._id ? msg : m)
1120
+ );
1121
+ };
1122
+ const onReaction = ({ messageId, reactions }) => {
1123
+ setMessages(
1124
+ (prev) => prev.map((m) => m._id === messageId ? { ...m, reactions } : m)
1125
+ );
1126
+ setThreadMessages(
1127
+ (prev) => prev.map((m) => m._id === messageId ? { ...m, reactions } : m)
1128
+ );
1129
+ };
1130
+ client.on("message:receive", onReceive);
1131
+ client.on("message:deleted", onDeleted);
1132
+ client.on("message:edited", onEdited);
1133
+ client.on("reaction:updated", onReaction);
1134
+ return () => {
1135
+ client.off("message:receive", onReceive);
1136
+ client.off("message:deleted", onDeleted);
1137
+ client.off("message:edited", onEdited);
1138
+ client.off("reaction:updated", onReaction);
1139
+ };
1140
+ }, [roomId, client, thread]);
1141
+ (0, import_react15.useEffect)(() => {
1142
+ if (!roomId || !client) return;
1143
+ const onStart = (event) => {
1144
+ if (event.roomId !== roomId) return;
1145
+ if (event.userId === client.currentUser?.userId) return;
1146
+ setTypingUsers(
1147
+ (prev) => new Map(prev).set(event.userId, event.displayName)
1148
+ );
1149
+ const existing = typingTimeouts.current.get(event.userId);
1150
+ if (existing) clearTimeout(existing);
1151
+ const t = setTimeout(() => {
1152
+ setTypingUsers((prev) => {
1153
+ const next = new Map(prev);
1154
+ next.delete(event.userId);
1155
+ return next;
1156
+ });
1157
+ }, 4e3);
1158
+ typingTimeouts.current.set(event.userId, t);
1159
+ };
1160
+ const onStop = (event) => {
1161
+ if (event.roomId !== roomId) return;
1162
+ setTypingUsers((prev) => {
1163
+ const next = new Map(prev);
1164
+ next.delete(event.userId);
1165
+ return next;
1166
+ });
1167
+ const existing = typingTimeouts.current.get(event.userId);
1168
+ if (existing) clearTimeout(existing);
1169
+ };
1170
+ client.on("typing:started", onStart);
1171
+ client.on("typing:stopped", onStop);
1172
+ return () => {
1173
+ client.off("typing:started", onStart);
1174
+ client.off("typing:stopped", onStop);
1175
+ typingTimeouts.current.forEach(clearTimeout);
1176
+ typingTimeouts.current.clear();
1177
+ setTypingUsers(/* @__PURE__ */ new Map());
1178
+ };
1179
+ }, [roomId, client]);
1180
+ const sendMessage = (0, import_react15.useCallback)(
1181
+ async (input) => {
1182
+ if (!roomId) throw new Error("No room selected");
1183
+ return client.sendMessage({ ...input, roomId });
1184
+ },
1185
+ [roomId, client]
1186
+ );
1187
+ const editMessage = (0, import_react15.useCallback)(
1188
+ async (messageId, text) => {
1189
+ if (!roomId) throw new Error("No room selected");
1190
+ return client.editMessage(messageId, roomId, text);
1191
+ },
1192
+ [roomId, client]
1193
+ );
1194
+ const deleteMessage = (0, import_react15.useCallback)(
1195
+ async (messageId) => {
1196
+ if (!roomId) throw new Error("No room selected");
1197
+ return client.deleteMessage(messageId, roomId);
1198
+ },
1199
+ [roomId, client]
1200
+ );
1201
+ const addReaction = (0, import_react15.useCallback)(
1202
+ async (messageId, emoji) => {
1203
+ if (!roomId) throw new Error("No room selected");
1204
+ return client.addReaction(messageId, roomId, emoji);
1205
+ },
1206
+ [roomId, client]
1207
+ );
1208
+ const loadMore = (0, import_react15.useCallback)(async () => {
1209
+ if (!roomId || loadingMore || !hasMore) return;
1210
+ setLoadingMore(true);
1211
+ try {
1212
+ const { messages: older, hasMore: more } = await client.getHistory(
1213
+ roomId,
1214
+ oldestMessageId.current
1215
+ );
1216
+ setMessages((prev) => [...older, ...prev]);
1217
+ setHasMore(more);
1218
+ if (older.length > 0) oldestMessageId.current = older[0]._id;
1219
+ } catch (err) {
1220
+ setError(err.message);
1221
+ } finally {
1222
+ setLoadingMore(false);
1223
+ }
1224
+ }, [roomId, loadingMore, hasMore, client]);
1225
+ const markRead = (0, import_react15.useCallback)(
1226
+ async (lastMessageId) => {
1227
+ if (!roomId) return;
1228
+ await client.markSeen(roomId, lastMessageId);
1229
+ },
1230
+ [roomId, client]
1231
+ );
1232
+ const openThread = (0, import_react15.useCallback)((message) => {
1233
+ setThread(message);
1234
+ setThreadMessages([]);
1235
+ setThreadHasMore(false);
1236
+ }, []);
1237
+ const closeThread = (0, import_react15.useCallback)(() => {
1238
+ setThread(null);
1239
+ setThreadMessages([]);
1240
+ }, []);
1241
+ const loadMoreThread = (0, import_react15.useCallback)(async () => {
1242
+ }, []);
1243
+ const startTyping = (0, import_react15.useCallback)(() => {
1244
+ if (!roomId) return;
1245
+ if (!isTypingRef.current) {
1246
+ client.startTyping(roomId);
1247
+ isTypingRef.current = true;
1248
+ }
1249
+ if (stopTypingTimeout.current) clearTimeout(stopTypingTimeout.current);
1250
+ stopTypingTimeout.current = setTimeout(() => {
1251
+ client.stopTyping(roomId);
1252
+ isTypingRef.current = false;
1253
+ }, 3e3);
1254
+ }, [roomId, client]);
1255
+ const stopTyping = (0, import_react15.useCallback)(() => {
1256
+ if (!roomId) return;
1257
+ if (stopTypingTimeout.current) clearTimeout(stopTypingTimeout.current);
1258
+ if (isTypingRef.current) {
1259
+ client.stopTyping(roomId);
1260
+ isTypingRef.current = false;
1261
+ }
1262
+ }, [roomId, client]);
1263
+ const typingText = (0, import_react15.useMemo)(() => {
1264
+ const names = Array.from(typingUsers.values());
1265
+ if (names.length === 0) return null;
1266
+ if (names.length === 1) return `${names[0]} is typing...`;
1267
+ if (names.length === 2) return `${names[0]} and ${names[1]} are typing...`;
1268
+ return `${names[0]} and ${names.length - 1} others are typing...`;
1269
+ }, [typingUsers]);
1270
+ const roomStateValue = (0, import_react15.useMemo)(
1271
+ () => ({
1272
+ room: { _id: roomId },
1273
+ messages,
1274
+ loading,
1275
+ loadingMore,
1276
+ hasMore,
1277
+ error,
1278
+ members: [],
1279
+ thread,
1280
+ threadMessages,
1281
+ threadHasMore,
1282
+ threadLoadingMore,
1283
+ pinnedMessages: messages.filter((m) => m.pinnedAt)
1284
+ }),
1285
+ [messages, loading, loadingMore, hasMore, error, thread, threadMessages, threadHasMore, threadLoadingMore, roomId]
1286
+ );
1287
+ const roomActionValue = (0, import_react15.useMemo)(
1288
+ () => ({
1289
+ sendMessage,
1290
+ editMessage,
1291
+ deleteMessage,
1292
+ addReaction,
1293
+ loadMore,
1294
+ markRead,
1295
+ openThread,
1296
+ closeThread,
1297
+ loadMoreThread
1298
+ }),
1299
+ [sendMessage, editMessage, deleteMessage, addReaction, loadMore, markRead, openThread, closeThread, loadMoreThread]
1300
+ );
1301
+ const typingValue = (0, import_react15.useMemo)(
1302
+ () => ({
1303
+ typingUsers,
1304
+ typingText,
1305
+ isAnyoneTyping: typingUsers.size > 0,
1306
+ startTyping,
1307
+ stopTyping
1308
+ }),
1309
+ [typingUsers, typingText, startTyping, stopTyping]
1310
+ );
1311
+ const componentOverrides = (0, import_react15.useMemo)(
1312
+ () => ({
1313
+ Avatar: Avatar2,
1314
+ Message: MessageOverride,
1315
+ MessageStatus: MessageStatus2,
1316
+ MessageActions: MessageActions2,
1317
+ DateSeparator: DateSeparator2,
1318
+ EmptyStateIndicator: EmptyStateIndicator2,
1319
+ LoadingIndicator: LoadingIndicator2,
1320
+ LoadingErrorIndicator: LoadingErrorIndicator2,
1321
+ ReactionPicker: ReactionPicker2,
1322
+ TypingIndicator: TypingIndicator2,
1323
+ MediaMessage: MediaMessage2,
1324
+ ThreadHeader: ThreadHeader2,
1325
+ Modal: Modal2,
1326
+ ChatInput: ChatInput2,
1327
+ RoomListItem,
1328
+ Search: Search2,
1329
+ OnlineBadge: OnlineBadge2
1330
+ }),
1331
+ [Avatar2, MessageOverride, MessageStatus2, MessageActions2, DateSeparator2, EmptyStateIndicator2, LoadingIndicator2, LoadingErrorIndicator2, ReactionPicker2, TypingIndicator2, MediaMessage2, ThreadHeader2, Modal2, ChatInput2, RoomListItem, Search2, OnlineBadge2]
1332
+ );
1333
+ const containerClass = customClasses?.room || "hermes-room";
1334
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(RoomStateProvider, { value: roomStateValue, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(RoomActionProvider, { value: roomActionValue, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(TypingProvider, { value: typingValue, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ComponentProvider, { value: componentOverrides, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: containerClass, children }) }) }) }) });
1335
+ };
1336
+
1337
+ // src/react/components/Window/Window.tsx
1338
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1339
+ var Window = ({
1340
+ className = "",
1341
+ children
1342
+ }) => {
1343
+ const { customClasses } = useChatContext("Window");
1344
+ const containerClass = customClasses?.window || `hermes-window ${className}`.trim();
1345
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
865
1346
  "div",
866
1347
  {
867
- ref,
1348
+ className: containerClass,
868
1349
  style: {
869
- position: "absolute",
870
- bottom: "calc(100% + 8px)",
871
- [isOwn ? "right" : "left"]: 0,
872
- zIndex: 100,
873
- background: "#1a1a2e",
874
- border: "1px solid rgba(255,255,255,0.1)",
875
- borderRadius: 14,
876
- padding: "8px 10px",
877
- boxShadow: "0 8px 32px rgba(0,0,0,0.4)",
878
- display: "grid",
879
- gridTemplateColumns: "repeat(5, 1fr)",
880
- gap: 4,
881
- animation: "hermes-pop 0.15s ease"
1350
+ display: "flex",
1351
+ flexDirection: "column",
1352
+ height: "100%",
1353
+ flex: 1,
1354
+ minWidth: 0,
1355
+ overflow: "hidden"
882
1356
  },
883
- children: REACTION_EMOJIS.map((emoji) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
884
- "button",
885
- {
886
- onClick: () => {
887
- onPick(emoji);
888
- onClose();
889
- },
890
- style: {
891
- background: "none",
892
- border: "none",
893
- cursor: "pointer",
894
- fontSize: 20,
895
- padding: "4px",
896
- borderRadius: 8,
897
- lineHeight: 1,
898
- transition: "transform 0.1s, background 0.1s"
899
- },
900
- onMouseEnter: (e) => {
901
- e.currentTarget.style.transform = "scale(1.3)";
902
- e.currentTarget.style.background = "rgba(255,255,255,0.08)";
903
- },
904
- onMouseLeave: (e) => {
905
- e.currentTarget.style.transform = "scale(1)";
906
- e.currentTarget.style.background = "none";
907
- },
908
- children: emoji
909
- },
910
- emoji
911
- ))
1357
+ children
912
1358
  }
913
1359
  );
914
1360
  };
915
- var TypingIndicator = ({ typingUsers }) => {
916
- if (!typingUsers.length) return null;
917
- const text = typingUsers.length === 1 ? `${typingUsers[0].displayName} is typing` : typingUsers.length === 2 ? `${typingUsers[0].displayName} and ${typingUsers[1].displayName} are typing` : `${typingUsers[0].displayName} and ${typingUsers.length - 1} others are typing`;
918
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1361
+
1362
+ // src/react/components/MessageList.tsx
1363
+ var import_react17 = __toESM(require("react"), 1);
1364
+
1365
+ // src/react/components/Message/Message.tsx
1366
+ var import_react16 = require("react");
1367
+
1368
+ // src/react/components/Avatar/Avatar.tsx
1369
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1370
+ var getInitials = (name) => {
1371
+ if (!name) return "?";
1372
+ const parts = name.trim().split(/\s+/);
1373
+ if (parts.length >= 2) return `${parts[0][0]}${parts[1][0]}`.toUpperCase();
1374
+ return name.slice(0, 2).toUpperCase();
1375
+ };
1376
+ var Avatar = ({
1377
+ image,
1378
+ name,
1379
+ size = 36,
1380
+ shape = "circle",
1381
+ className = "",
1382
+ online
1383
+ }) => {
1384
+ const borderRadius = shape === "circle" ? "50%" : shape === "rounded" ? "8px" : "0";
1385
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
919
1386
  "div",
920
1387
  {
1388
+ className: `hermes-avatar ${className}`,
921
1389
  style: {
922
- display: "flex",
923
- alignItems: "center",
924
- gap: 8,
925
- padding: "6px 16px 2px",
926
- minHeight: 28
1390
+ position: "relative",
1391
+ width: size,
1392
+ height: size,
1393
+ flexShrink: 0
927
1394
  },
928
1395
  children: [
929
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1396
+ image ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1397
+ "img",
1398
+ {
1399
+ src: image,
1400
+ alt: name || "avatar",
1401
+ style: {
1402
+ width: size,
1403
+ height: size,
1404
+ borderRadius,
1405
+ objectFit: "cover",
1406
+ display: "block"
1407
+ }
1408
+ }
1409
+ ) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
930
1410
  "div",
931
1411
  {
932
1412
  style: {
1413
+ width: size,
1414
+ height: size,
1415
+ borderRadius,
1416
+ background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
933
1417
  display: "flex",
934
1418
  alignItems: "center",
935
- gap: 3,
936
- background: "#f0f0f0",
937
- borderRadius: 12,
938
- padding: "6px 10px"
1419
+ justifyContent: "center",
1420
+ color: "#fff",
1421
+ fontWeight: 700,
1422
+ fontSize: size * 0.38,
1423
+ userSelect: "none"
939
1424
  },
940
- children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
941
- "span",
942
- {
943
- style: {
944
- width: 6,
945
- height: 6,
946
- borderRadius: "50%",
947
- background: "#999",
948
- display: "block",
949
- animation: `hermes-bounce 1.2s ease-in-out ${i * 0.18}s infinite`
950
- }
951
- },
952
- i
953
- ))
1425
+ children: getInitials(name)
954
1426
  }
955
1427
  ),
956
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: 11, color: "#999" }, children: text })
1428
+ online !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1429
+ "span",
1430
+ {
1431
+ style: {
1432
+ position: "absolute",
1433
+ bottom: 0,
1434
+ right: 0,
1435
+ width: size * 0.3,
1436
+ height: size * 0.3,
1437
+ borderRadius: "50%",
1438
+ background: online ? "#22c55e" : "#9ca3af",
1439
+ border: "2px solid #fff"
1440
+ }
1441
+ }
1442
+ )
957
1443
  ]
958
1444
  }
959
1445
  );
960
1446
  };
961
- var DefaultMessage = ({ message, isOwn, onEdit, onDelete, onReact, onReply, renderAvatar }) => {
962
- const [hovered, setHovered] = (0, import_react8.useState)(false);
963
- const [pickerOpen, setPickerOpen] = (0, import_react8.useState)(false);
1447
+
1448
+ // src/react/components/Message/MessageStatus.tsx
1449
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1450
+ var MessageStatus = ({
1451
+ status,
1452
+ seenCount = 0,
1453
+ isMyMessage = true,
1454
+ className = ""
1455
+ }) => {
1456
+ if (!isMyMessage) return null;
1457
+ const color = status === "seen" ? "#0084ff" : "rgba(128,128,128,0.6)";
1458
+ const checks = status === "sent" ? "\u2713" : "\u2713\u2713";
1459
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1460
+ "span",
1461
+ {
1462
+ className: `hermes-message-status ${className}`,
1463
+ title: status === "seen" ? `Seen by ${seenCount} ${seenCount === 1 ? "person" : "people"}` : status === "delivered" ? "Delivered" : "Sent",
1464
+ style: {
1465
+ fontSize: 11,
1466
+ color,
1467
+ marginLeft: 4,
1468
+ userSelect: "none"
1469
+ },
1470
+ children: checks
1471
+ }
1472
+ );
1473
+ };
1474
+
1475
+ // src/react/components/Message/MessageActions.tsx
1476
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1477
+ var ActionBtn = ({ onClick, title, children }) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1478
+ "button",
1479
+ {
1480
+ onClick,
1481
+ title,
1482
+ style: {
1483
+ background: "#fff",
1484
+ border: "1px solid rgba(0,0,0,0.1)",
1485
+ borderRadius: 8,
1486
+ cursor: "pointer",
1487
+ fontSize: 14,
1488
+ padding: "3px 6px",
1489
+ lineHeight: 1,
1490
+ boxShadow: "0 1px 4px rgba(0,0,0,0.1)",
1491
+ transition: "transform 0.1s"
1492
+ },
1493
+ onMouseEnter: (e) => e.currentTarget.style.transform = "scale(1.15)",
1494
+ onMouseLeave: (e) => e.currentTarget.style.transform = "scale(1)",
1495
+ children
1496
+ }
1497
+ );
1498
+ var MessageActions = ({
1499
+ isOwn,
1500
+ isText,
1501
+ hasThread = false,
1502
+ replyCount = 0,
1503
+ onReact,
1504
+ onReply,
1505
+ onThread,
1506
+ onEdit,
1507
+ onDelete,
1508
+ onPin,
1509
+ className = ""
1510
+ }) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1511
+ "div",
1512
+ {
1513
+ className: `hermes-message-actions ${className}`,
1514
+ style: {
1515
+ display: "flex",
1516
+ flexDirection: isOwn ? "row-reverse" : "row",
1517
+ gap: 2
1518
+ },
1519
+ children: [
1520
+ onReact && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ActionBtn, { onClick: onReact, title: "React", children: "\u{1F60A}" }),
1521
+ onReply && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ActionBtn, { onClick: onReply, title: "Reply", children: "\u21A9" }),
1522
+ onThread && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ActionBtn, { onClick: onThread, title: `Thread${replyCount > 0 ? ` (${replyCount})` : ""}`, children: "\u{1F9F5}" }),
1523
+ isOwn && isText && onEdit && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ActionBtn, { onClick: onEdit, title: "Edit", children: "\u270F\uFE0F" }),
1524
+ onPin && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ActionBtn, { onClick: onPin, title: "Pin", children: "\u{1F4CC}" }),
1525
+ isOwn && onDelete && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ActionBtn, { onClick: onDelete, title: "Delete", children: "\u{1F5D1}" })
1526
+ ]
1527
+ }
1528
+ );
1529
+
1530
+ // src/react/components/Message/Message.tsx
1531
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1532
+ var REACTION_EMOJIS = ["\u{1F44D}", "\u2764\uFE0F", "\u{1F602}", "\u{1F62E}", "\u{1F622}", "\u{1F525}", "\u{1F389}", "\u{1F44F}"];
1533
+ var formatTime = (iso) => new Date(iso).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
1534
+ var formatFileSize = (bytes) => {
1535
+ if (!bytes) return "";
1536
+ if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
1537
+ if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1538
+ return `${bytes} B`;
1539
+ };
1540
+ var Message = ({
1541
+ message,
1542
+ isOwn,
1543
+ onEdit,
1544
+ onDelete,
1545
+ onReact,
1546
+ onReply,
1547
+ onOpenThread,
1548
+ onPin,
1549
+ renderAvatar,
1550
+ senderName,
1551
+ senderImage,
1552
+ groupStyle = "single",
1553
+ className = "",
1554
+ showAvatar = true
1555
+ }) => {
1556
+ const [hovered, setHovered] = (0, import_react16.useState)(false);
1557
+ const [pickerOpen, setPickerOpen] = (0, import_react16.useState)(false);
1558
+ const pickerRef = (0, import_react16.useRef)(null);
1559
+ (0, import_react16.useEffect)(() => {
1560
+ if (!pickerOpen) return;
1561
+ const handler = (e) => {
1562
+ if (pickerRef.current && !pickerRef.current.contains(e.target)) {
1563
+ setPickerOpen(false);
1564
+ }
1565
+ };
1566
+ document.addEventListener("mousedown", handler);
1567
+ return () => document.removeEventListener("mousedown", handler);
1568
+ }, [pickerOpen]);
964
1569
  if (message.isDeleted) {
965
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1570
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
966
1571
  "div",
967
1572
  {
1573
+ className: `hermes-message hermes-message--deleted ${className}`,
968
1574
  style: {
969
1575
  opacity: 0.5,
970
1576
  fontStyle: "italic",
@@ -975,40 +1581,24 @@ var DefaultMessage = ({ message, isOwn, onEdit, onDelete, onReact, onReply, rend
975
1581
  }
976
1582
  );
977
1583
  }
978
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1584
+ const showAvatarSlot = showAvatar && !isOwn && (groupStyle === "bottom" || groupStyle === "single");
1585
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
979
1586
  "div",
980
1587
  {
1588
+ className: `hermes-message ${isOwn ? "hermes-message--own" : "hermes-message--other"} ${className}`,
981
1589
  onMouseEnter: () => setHovered(true),
982
- onMouseLeave: () => {
983
- setHovered(false);
984
- },
1590
+ onMouseLeave: () => setHovered(false),
985
1591
  style: {
986
1592
  display: "flex",
987
1593
  flexDirection: isOwn ? "row-reverse" : "row",
988
1594
  alignItems: "flex-end",
989
1595
  gap: 8,
990
- marginBottom: 4,
1596
+ marginBottom: groupStyle === "bottom" || groupStyle === "single" ? 8 : 2,
991
1597
  position: "relative"
992
1598
  },
993
1599
  children: [
994
- !isOwn && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flexShrink: 0 }, children: renderAvatar ? renderAvatar(message.senderId) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
995
- "div",
996
- {
997
- style: {
998
- width: 32,
999
- height: 32,
1000
- borderRadius: "50%",
1001
- background: "#e0e0e0",
1002
- display: "flex",
1003
- alignItems: "center",
1004
- justifyContent: "center",
1005
- fontSize: 12,
1006
- fontWeight: 600
1007
- },
1008
- children: message.senderId.slice(-2).toUpperCase()
1009
- }
1010
- ) }),
1011
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1600
+ !isOwn && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { flexShrink: 0, width: 32 }, children: showAvatarSlot ? renderAvatar ? renderAvatar(message.senderId) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Avatar, { image: senderImage, name: senderName || message.senderId, size: 32 }) : null }),
1601
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1012
1602
  "div",
1013
1603
  {
1014
1604
  style: {
@@ -1018,13 +1608,10 @@ var DefaultMessage = ({ message, isOwn, onEdit, onDelete, onReact, onReply, rend
1018
1608
  alignItems: isOwn ? "flex-end" : "flex-start"
1019
1609
  },
1020
1610
  children: [
1021
- (onEdit || onDelete || onReact || onReply) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1611
+ (onEdit || onDelete || onReact || onReply || onOpenThread) && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1022
1612
  "div",
1023
1613
  {
1024
1614
  style: {
1025
- display: "flex",
1026
- flexDirection: isOwn ? "row-reverse" : "row",
1027
- gap: 2,
1028
1615
  marginBottom: 4,
1029
1616
  opacity: hovered ? 1 : 0,
1030
1617
  pointerEvents: hovered ? "auto" : "none",
@@ -1032,55 +1619,86 @@ var DefaultMessage = ({ message, isOwn, onEdit, onDelete, onReact, onReply, rend
1032
1619
  position: "relative"
1033
1620
  },
1034
1621
  children: [
1035
- onReact && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { position: "relative" }, children: [
1036
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1037
- ActionBtn,
1038
- {
1039
- onClick: () => setPickerOpen((p) => !p),
1040
- title: "React",
1041
- children: "\u{1FAE0}"
1042
- }
1043
- ),
1044
- pickerOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1045
- EmojiPicker,
1046
- {
1047
- isOwn,
1048
- onPick: (emoji) => onReact(message._id, emoji),
1049
- onClose: () => setPickerOpen(false)
1050
- }
1051
- )
1052
- ] }),
1053
- onReply && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ActionBtn, { onClick: () => onReply(message), title: "Reply", children: "\u21A9" }),
1054
- isOwn && onEdit && message.type === "text" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1055
- ActionBtn,
1622
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1623
+ MessageActions,
1056
1624
  {
1057
- onClick: () => {
1625
+ isOwn,
1626
+ isText: message.type === "text",
1627
+ hasThread: !!message.replyCount && message.replyCount > 0,
1628
+ replyCount: message.replyCount,
1629
+ onReact: onReact ? () => setPickerOpen((p) => !p) : void 0,
1630
+ onReply: onReply ? () => onReply(message) : void 0,
1631
+ onThread: onOpenThread ? () => onOpenThread(message) : void 0,
1632
+ onEdit: onEdit ? () => {
1058
1633
  const text = window.prompt("Edit message:", message.text);
1059
1634
  if (text) onEdit(message._id, text);
1060
- },
1061
- title: "Edit",
1062
- children: "\u270F\uFE0F"
1635
+ } : void 0,
1636
+ onDelete: onDelete ? () => onDelete(message._id) : void 0,
1637
+ onPin: onPin ? () => onPin(message) : void 0
1063
1638
  }
1064
1639
  ),
1065
- isOwn && onDelete && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ActionBtn, { onClick: () => onDelete(message._id), title: "Delete", children: "\u{1F5D1}" })
1640
+ pickerOpen && onReact && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1641
+ "div",
1642
+ {
1643
+ ref: pickerRef,
1644
+ style: {
1645
+ position: "absolute",
1646
+ bottom: "calc(100% + 4px)",
1647
+ [isOwn ? "right" : "left"]: 0,
1648
+ zIndex: 100,
1649
+ background: "#1a1a2e",
1650
+ border: "1px solid rgba(255,255,255,0.1)",
1651
+ borderRadius: 14,
1652
+ padding: "8px 10px",
1653
+ boxShadow: "0 8px 32px rgba(0,0,0,0.4)",
1654
+ display: "flex",
1655
+ gap: 4,
1656
+ animation: "hermes-pop 0.15s ease"
1657
+ },
1658
+ children: REACTION_EMOJIS.map((emoji) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1659
+ "button",
1660
+ {
1661
+ onClick: () => {
1662
+ onReact(message._id, emoji);
1663
+ setPickerOpen(false);
1664
+ },
1665
+ style: {
1666
+ background: "none",
1667
+ border: "none",
1668
+ cursor: "pointer",
1669
+ fontSize: 20,
1670
+ padding: "4px",
1671
+ borderRadius: 8,
1672
+ lineHeight: 1,
1673
+ transition: "transform 0.1s"
1674
+ },
1675
+ onMouseEnter: (e) => e.currentTarget.style.transform = "scale(1.3)",
1676
+ onMouseLeave: (e) => e.currentTarget.style.transform = "scale(1)",
1677
+ children: emoji
1678
+ },
1679
+ emoji
1680
+ ))
1681
+ }
1682
+ )
1066
1683
  ]
1067
1684
  }
1068
1685
  ),
1069
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1686
+ !isOwn && (groupStyle === "top" || groupStyle === "single") && senderName && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { fontSize: 11, fontWeight: 600, opacity: 0.6, marginBottom: 2, marginLeft: 4 }, children: senderName }),
1687
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1070
1688
  "div",
1071
1689
  {
1072
1690
  style: {
1073
1691
  padding: "8px 12px",
1074
- borderRadius: isOwn ? "16px 16px 4px 16px" : "16px 16px 16px 4px",
1692
+ borderRadius: isOwn ? groupStyle === "top" || groupStyle === "single" ? "16px 16px 4px 16px" : "16px 4px 4px 16px" : groupStyle === "top" || groupStyle === "single" ? "16px 16px 16px 4px" : "4px 16px 16px 4px",
1075
1693
  background: isOwn ? "#0084ff" : "#f0f0f0",
1076
1694
  color: isOwn ? "#fff" : "#000"
1077
1695
  },
1078
1696
  children: [
1079
- message.replyTo && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1697
+ message.replyTo && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1080
1698
  "div",
1081
1699
  {
1082
1700
  style: {
1083
- borderLeft: "3px solid rgba(255,255,255,0.4)",
1701
+ borderLeft: `3px solid ${isOwn ? "rgba(255,255,255,0.4)" : "rgba(0,132,255,0.4)"}`,
1084
1702
  paddingLeft: 8,
1085
1703
  marginBottom: 6,
1086
1704
  fontSize: 12,
@@ -1089,27 +1707,24 @@ var DefaultMessage = ({ message, isOwn, onEdit, onDelete, onReact, onReply, rend
1089
1707
  children: "Replying to a message"
1090
1708
  }
1091
1709
  ),
1092
- message.type === "text" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: { margin: 0, wordBreak: "break-word" }, children: [
1710
+ message.type === "text" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("p", { style: { margin: 0, wordBreak: "break-word" }, children: [
1093
1711
  message.text,
1094
- message.editedAt && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: 10, opacity: 0.6, marginLeft: 6 }, children: "(edited)" })
1712
+ message.editedAt && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { fontSize: 10, opacity: 0.6, marginLeft: 6 }, children: "(edited)" })
1095
1713
  ] }),
1096
- message.type === "link" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1097
- message.text && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { margin: "0 0 4px" }, children: message.text }),
1098
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1714
+ message.type === "link" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
1715
+ message.text && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { style: { margin: "0 0 4px" }, children: message.text }),
1716
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1099
1717
  "a",
1100
1718
  {
1101
1719
  href: message.url,
1102
1720
  target: "_blank",
1103
1721
  rel: "noopener noreferrer",
1104
- style: {
1105
- color: isOwn ? "#cce4ff" : "#0084ff",
1106
- wordBreak: "break-all"
1107
- },
1722
+ style: { color: isOwn ? "#cce4ff" : "#0084ff", wordBreak: "break-all" },
1108
1723
  children: message.url
1109
1724
  }
1110
1725
  )
1111
1726
  ] }),
1112
- message.type === "image" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1727
+ message.type === "image" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1113
1728
  "img",
1114
1729
  {
1115
1730
  src: message.url,
@@ -1117,16 +1732,17 @@ var DefaultMessage = ({ message, isOwn, onEdit, onDelete, onReact, onReply, rend
1117
1732
  style: { maxWidth: "100%", borderRadius: 8, display: "block" }
1118
1733
  }
1119
1734
  ),
1120
- message.type === "video" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1735
+ message.type === "video" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1121
1736
  "video",
1122
1737
  {
1123
1738
  src: message.url,
1739
+ poster: message.thumbnail,
1124
1740
  controls: true,
1125
1741
  style: { maxWidth: "100%", borderRadius: 8 }
1126
1742
  }
1127
1743
  ),
1128
- message.type === "audio" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("audio", { src: message.url, controls: true, style: { width: "100%" } }),
1129
- message.type === "document" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1744
+ message.type === "audio" && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("audio", { src: message.url, controls: true, style: { width: "100%" } }),
1745
+ message.type === "document" && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1130
1746
  "a",
1131
1747
  {
1132
1748
  href: message.url,
@@ -1140,67 +1756,90 @@ var DefaultMessage = ({ message, isOwn, onEdit, onDelete, onReact, onReply, rend
1140
1756
  textDecoration: "none"
1141
1757
  },
1142
1758
  children: [
1143
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: 24 }, children: "\u{1F4C4}" }),
1144
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
1145
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontWeight: 600, fontSize: 13 }, children: message.fileName }),
1146
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 11, opacity: 0.7 }, children: formatFileSize(message.fileSize) })
1759
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { fontSize: 24 }, children: "\u{1F4C4}" }),
1760
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
1761
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { fontWeight: 600, fontSize: 13 }, children: message.fileName }),
1762
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { fontSize: 11, opacity: 0.7 }, children: formatFileSize(message.fileSize) })
1147
1763
  ] })
1148
1764
  ]
1149
1765
  }
1150
1766
  ),
1151
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1767
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1152
1768
  "div",
1153
1769
  {
1154
1770
  style: {
1771
+ display: "flex",
1772
+ alignItems: "center",
1773
+ justifyContent: "flex-end",
1774
+ gap: 4,
1155
1775
  fontSize: 10,
1156
1776
  opacity: 0.6,
1157
- textAlign: "right",
1158
1777
  marginTop: 4
1159
1778
  },
1160
- children: formatTime(message.createdAt)
1779
+ children: [
1780
+ formatTime(message.createdAt),
1781
+ message.pinnedAt && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { title: "Pinned", children: "\u{1F4CC}" }),
1782
+ isOwn && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1783
+ MessageStatus,
1784
+ {
1785
+ status: message.deliveryStatus,
1786
+ seenCount: message.seenBy?.length || 0,
1787
+ isMyMessage: isOwn
1788
+ }
1789
+ )
1790
+ ]
1161
1791
  }
1162
1792
  )
1163
1793
  ]
1164
1794
  }
1165
1795
  ),
1166
- message.reactions?.filter((r) => r.users.length > 0).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1167
- "div",
1796
+ message.replyCount && message.replyCount > 0 && onOpenThread && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1797
+ "button",
1168
1798
  {
1169
- style: { display: "flex", gap: 4, flexWrap: "wrap", marginTop: 4 },
1170
- children: message.reactions.filter((r) => r.users.length > 0).map((r) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1171
- "span",
1172
- {
1173
- onClick: () => onReact?.(message._id, r.emoji),
1174
- style: {
1175
- background: "#f0f0f0",
1176
- border: "1px solid rgba(0,0,0,0.08)",
1177
- borderRadius: 20,
1178
- padding: "2px 8px",
1179
- fontSize: 13,
1180
- cursor: "pointer",
1181
- display: "flex",
1182
- alignItems: "center",
1183
- gap: 4,
1184
- transition: "transform 0.1s",
1185
- userSelect: "none"
1186
- },
1187
- onMouseEnter: (e) => e.currentTarget.style.transform = "scale(1.1)",
1188
- onMouseLeave: (e) => e.currentTarget.style.transform = "scale(1)",
1189
- children: [
1190
- r.emoji,
1191
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1192
- "span",
1193
- {
1194
- style: { fontSize: 11, fontWeight: 600, color: "#555" },
1195
- children: r.users.length
1196
- }
1197
- )
1198
- ]
1199
- },
1200
- r.emoji
1201
- ))
1799
+ onClick: () => onOpenThread(message),
1800
+ style: {
1801
+ background: "none",
1802
+ border: "none",
1803
+ color: "#0084ff",
1804
+ cursor: "pointer",
1805
+ fontSize: 12,
1806
+ fontWeight: 600,
1807
+ padding: "2px 4px",
1808
+ marginTop: 2
1809
+ },
1810
+ children: [
1811
+ message.replyCount,
1812
+ " ",
1813
+ message.replyCount === 1 ? "reply" : "replies"
1814
+ ]
1202
1815
  }
1203
- )
1816
+ ),
1817
+ message.reactions?.filter((r) => r.users.length > 0).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { style: { display: "flex", gap: 4, flexWrap: "wrap", marginTop: 4 }, children: message.reactions.filter((r) => r.users.length > 0).map((r) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1818
+ "span",
1819
+ {
1820
+ onClick: () => onReact?.(message._id, r.emoji),
1821
+ style: {
1822
+ background: "#f0f0f0",
1823
+ border: "1px solid rgba(0,0,0,0.08)",
1824
+ borderRadius: 20,
1825
+ padding: "2px 8px",
1826
+ fontSize: 13,
1827
+ cursor: onReact ? "pointer" : "default",
1828
+ display: "flex",
1829
+ alignItems: "center",
1830
+ gap: 4,
1831
+ transition: "transform 0.1s",
1832
+ userSelect: "none"
1833
+ },
1834
+ onMouseEnter: (e) => e.currentTarget.style.transform = "scale(1.1)",
1835
+ onMouseLeave: (e) => e.currentTarget.style.transform = "scale(1)",
1836
+ children: [
1837
+ r.emoji,
1838
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { style: { fontSize: 11, fontWeight: 600, color: "#555" }, children: r.users.length })
1839
+ ]
1840
+ },
1841
+ r.emoji
1842
+ )) })
1204
1843
  ]
1205
1844
  }
1206
1845
  )
@@ -1208,52 +1847,277 @@ var DefaultMessage = ({ message, isOwn, onEdit, onDelete, onReact, onReply, rend
1208
1847
  }
1209
1848
  );
1210
1849
  };
1211
- var ActionBtn = ({ onClick, title, children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1212
- "button",
1850
+
1851
+ // src/react/components/DateSeparator/DateSeparator.tsx
1852
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1853
+ var defaultFormat = (date) => {
1854
+ const now = /* @__PURE__ */ new Date();
1855
+ const isToday = date.getDate() === now.getDate() && date.getMonth() === now.getMonth() && date.getFullYear() === now.getFullYear();
1856
+ const yesterday = new Date(now);
1857
+ yesterday.setDate(yesterday.getDate() - 1);
1858
+ const isYesterday = date.getDate() === yesterday.getDate() && date.getMonth() === yesterday.getMonth() && date.getFullYear() === yesterday.getFullYear();
1859
+ if (isToday) return "Today";
1860
+ if (isYesterday) return "Yesterday";
1861
+ return date.toLocaleDateString(void 0, {
1862
+ weekday: "long",
1863
+ year: "numeric",
1864
+ month: "long",
1865
+ day: "numeric"
1866
+ });
1867
+ };
1868
+ var DateSeparator = ({
1869
+ date,
1870
+ formatDate = defaultFormat,
1871
+ className = ""
1872
+ }) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1873
+ "div",
1213
1874
  {
1214
- onClick,
1215
- title,
1875
+ className: `hermes-date-separator ${className}`,
1216
1876
  style: {
1217
- background: "#fff",
1218
- border: "1px solid rgba(0,0,0,0.1)",
1219
- borderRadius: 8,
1220
- cursor: "pointer",
1221
- fontSize: 14,
1222
- padding: "3px 6px",
1223
- lineHeight: 1,
1224
- boxShadow: "0 1px 4px rgba(0,0,0,0.1)",
1225
- transition: "transform 0.1s"
1877
+ display: "flex",
1878
+ alignItems: "center",
1879
+ gap: 12,
1880
+ padding: "16px 0"
1226
1881
  },
1227
- onMouseEnter: (e) => e.currentTarget.style.transform = "scale(1.15)",
1228
- onMouseLeave: (e) => e.currentTarget.style.transform = "scale(1)",
1229
- children
1882
+ children: [
1883
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1884
+ "div",
1885
+ {
1886
+ style: { flex: 1, height: 1, background: "rgba(128,128,128,0.2)" }
1887
+ }
1888
+ ),
1889
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1890
+ "span",
1891
+ {
1892
+ style: {
1893
+ fontSize: 12,
1894
+ fontWeight: 600,
1895
+ color: "rgba(128,128,128,0.7)",
1896
+ whiteSpace: "nowrap",
1897
+ userSelect: "none"
1898
+ },
1899
+ children: formatDate(date)
1900
+ }
1901
+ ),
1902
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
1903
+ "div",
1904
+ {
1905
+ style: { flex: 1, height: 1, background: "rgba(128,128,128,0.2)" }
1906
+ }
1907
+ )
1908
+ ]
1230
1909
  }
1231
1910
  );
1232
- var MessageList = ({
1233
- messages,
1234
- currentUser,
1235
- loading = false,
1236
- loadingMore = false,
1237
- hasMore = false,
1238
- onLoadMore,
1239
- onEdit,
1240
- onDelete,
1241
- onReact,
1242
- onReply,
1243
- renderMessage,
1244
- renderAvatar,
1245
- className = "",
1246
- autoScroll = true,
1247
- typingUsers = []
1911
+
1912
+ // src/react/components/EmptyStateIndicator/EmptyStateIndicator.tsx
1913
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1914
+ var DEFAULT_TEXTS = {
1915
+ message: "No messages yet. Say hello! \u{1F44B}",
1916
+ room: "No conversations yet.",
1917
+ thread: "No replies yet.",
1918
+ search: "No results found."
1919
+ };
1920
+ var EmptyStateIndicator = ({
1921
+ listType = "message",
1922
+ text,
1923
+ className = ""
1924
+ }) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
1925
+ "div",
1926
+ {
1927
+ className: `hermes-empty-state ${className}`,
1928
+ style: {
1929
+ display: "flex",
1930
+ flexDirection: "column",
1931
+ alignItems: "center",
1932
+ justifyContent: "center",
1933
+ gap: 8,
1934
+ padding: 32,
1935
+ textAlign: "center",
1936
+ opacity: 0.5,
1937
+ flex: 1
1938
+ },
1939
+ children: [
1940
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: { fontSize: 36 }, children: listType === "message" ? "\u{1F4AC}" : listType === "room" ? "\u{1F4ED}" : listType === "thread" ? "\u{1F9F5}" : "\u{1F50D}" }),
1941
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { style: { fontSize: 14 }, children: text || DEFAULT_TEXTS[listType] })
1942
+ ]
1943
+ }
1944
+ );
1945
+
1946
+ // src/react/components/Loading/LoadingIndicator.tsx
1947
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1948
+ var LoadingIndicator = ({
1949
+ size = 32,
1950
+ color = "#0084ff",
1951
+ text,
1952
+ className = ""
1953
+ }) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
1954
+ "div",
1955
+ {
1956
+ className: `hermes-loading ${className}`,
1957
+ style: {
1958
+ display: "flex",
1959
+ flexDirection: "column",
1960
+ alignItems: "center",
1961
+ justifyContent: "center",
1962
+ gap: 8,
1963
+ padding: 16
1964
+ },
1965
+ children: [
1966
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1967
+ "div",
1968
+ {
1969
+ style: {
1970
+ width: size,
1971
+ height: size,
1972
+ border: `3px solid rgba(128,128,128,0.15)`,
1973
+ borderTopColor: color,
1974
+ borderRadius: "50%",
1975
+ animation: "hermes-spin 0.8s linear infinite"
1976
+ }
1977
+ }
1978
+ ),
1979
+ text && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { fontSize: 13, opacity: 0.6 }, children: text }),
1980
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("style", { children: `
1981
+ @keyframes hermes-spin {
1982
+ to { transform: rotate(360deg); }
1983
+ }
1984
+ ` })
1985
+ ]
1986
+ }
1987
+ );
1988
+ var LoadingErrorIndicator = ({
1989
+ error,
1990
+ onRetry,
1991
+ className = ""
1248
1992
  }) => {
1249
- const bottomRef = (0, import_react8.useRef)(null);
1250
- const containerRef = (0, import_react8.useRef)(null);
1251
- (0, import_react8.useEffect)(() => {
1993
+ if (!error) return null;
1994
+ const message = typeof error === "string" ? error : error.message;
1995
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
1996
+ "div",
1997
+ {
1998
+ className: `hermes-loading-error ${className}`,
1999
+ style: {
2000
+ display: "flex",
2001
+ flexDirection: "column",
2002
+ alignItems: "center",
2003
+ justifyContent: "center",
2004
+ gap: 8,
2005
+ padding: 24,
2006
+ textAlign: "center"
2007
+ },
2008
+ children: [
2009
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { fontSize: 28 }, children: "\u26A0\uFE0F" }),
2010
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { fontSize: 14, opacity: 0.7 }, children: message }),
2011
+ onRetry && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
2012
+ "button",
2013
+ {
2014
+ onClick: onRetry,
2015
+ style: {
2016
+ marginTop: 4,
2017
+ padding: "6px 16px",
2018
+ border: "1px solid rgba(128,128,128,0.3)",
2019
+ borderRadius: 8,
2020
+ background: "none",
2021
+ cursor: "pointer",
2022
+ fontSize: 13,
2023
+ fontWeight: 600
2024
+ },
2025
+ children: "Retry"
2026
+ }
2027
+ )
2028
+ ]
2029
+ }
2030
+ );
2031
+ };
2032
+
2033
+ // src/react/components/TypingIndicator.tsx
2034
+ var import_jsx_runtime17 = require("react/jsx-runtime");
2035
+ var TypingIndicator = ({
2036
+ typingText,
2037
+ className = ""
2038
+ }) => {
2039
+ if (!typingText) return null;
2040
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
2041
+ "div",
2042
+ {
2043
+ className: `hermes-typing-indicator ${className}`,
2044
+ style: {
2045
+ display: "flex",
2046
+ alignItems: "center",
2047
+ gap: 6,
2048
+ padding: "4px 16px",
2049
+ minHeight: 24
2050
+ },
2051
+ children: [
2052
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: { display: "flex", gap: 3 }, children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
2053
+ "span",
2054
+ {
2055
+ style: {
2056
+ width: 6,
2057
+ height: 6,
2058
+ borderRadius: "50%",
2059
+ background: "#aaa",
2060
+ display: "block",
2061
+ animation: `hermes-bounce 1.2s ease-in-out ${i * 0.2}s infinite`
2062
+ }
2063
+ },
2064
+ i
2065
+ )) }),
2066
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { style: { fontSize: 12, opacity: 0.6 }, children: typingText }),
2067
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("style", { children: `
2068
+ @keyframes hermes-bounce {
2069
+ 0%, 80%, 100% { transform: translateY(0); }
2070
+ 40% { transform: translateY(-4px); }
2071
+ }
2072
+ ` })
2073
+ ]
2074
+ }
2075
+ );
2076
+ };
2077
+
2078
+ // src/react/components/MessageList.tsx
2079
+ var import_jsx_runtime18 = require("react/jsx-runtime");
2080
+ var isSameDay = (d1, d2) => {
2081
+ const a = new Date(d1);
2082
+ const b = new Date(d2);
2083
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
2084
+ };
2085
+ var MessageList = (props) => {
2086
+ const chatCtx = useChatContext("MessageList");
2087
+ const roomStateCtx = useRoomStateContext("MessageList");
2088
+ const roomActionCtx = useRoomActionContext("MessageList");
2089
+ const typingCtx = useTypingContext("MessageList");
2090
+ const componentCtx = useComponentContext("MessageList");
2091
+ const messages = props.messages ?? roomStateCtx.messages ?? [];
2092
+ const currentUser = props.currentUser ?? chatCtx.currentUser;
2093
+ const loading = props.loading ?? roomStateCtx.loading ?? false;
2094
+ const loadingMore = props.loadingMore ?? roomStateCtx.loadingMore ?? false;
2095
+ const hasMore = props.hasMore ?? roomStateCtx.hasMore ?? false;
2096
+ const onLoadMore = props.onLoadMore ?? roomActionCtx.loadMore;
2097
+ const onEdit = props.onEdit ?? (roomActionCtx.editMessage ? (id, text) => roomActionCtx.editMessage(id, text) : void 0);
2098
+ const onDelete = props.onDelete ?? (roomActionCtx.deleteMessage ? (id) => roomActionCtx.deleteMessage(id) : void 0);
2099
+ const onReact = props.onReact ?? (roomActionCtx.addReaction ? (id, emoji) => roomActionCtx.addReaction(id, emoji) : void 0);
2100
+ const onReply = props.onReply;
2101
+ const onOpenThread = props.onOpenThread ?? (roomActionCtx.openThread ? (msg) => roomActionCtx.openThread(msg) : void 0);
2102
+ const autoScroll = props.autoScroll ?? true;
2103
+ const disableDateSeparator = props.disableDateSeparator ?? false;
2104
+ const className = props.className ?? "";
2105
+ const renderMessage = props.renderMessage;
2106
+ const renderAvatar = props.renderAvatar;
2107
+ const typingText = props.typingText ?? typingCtx.typingText ?? null;
2108
+ const MessageComponent = componentCtx.Message || Message;
2109
+ const DateSepComponent = componentCtx.DateSeparator || DateSeparator;
2110
+ const EmptyComponent = componentCtx.EmptyStateIndicator || EmptyStateIndicator;
2111
+ const LoadingComponent = componentCtx.LoadingIndicator || LoadingIndicator;
2112
+ const TypingComponent = componentCtx.TypingIndicator || TypingIndicator;
2113
+ const bottomRef = (0, import_react17.useRef)(null);
2114
+ const containerRef = (0, import_react17.useRef)(null);
2115
+ (0, import_react17.useEffect)(() => {
1252
2116
  if (autoScroll && bottomRef.current) {
1253
2117
  bottomRef.current.scrollIntoView({ behavior: "smooth" });
1254
2118
  }
1255
2119
  }, [messages, autoScroll]);
1256
- (0, import_react8.useEffect)(() => {
2120
+ (0, import_react17.useEffect)(() => {
1257
2121
  const container = containerRef.current;
1258
2122
  if (!container || !onLoadMore) return;
1259
2123
  const onScroll = () => {
@@ -1263,21 +2127,33 @@ var MessageList = ({
1263
2127
  return () => container.removeEventListener("scroll", onScroll);
1264
2128
  }, [hasMore, loadingMore, onLoadMore]);
1265
2129
  if (loading) {
1266
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2130
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1267
2131
  "div",
1268
2132
  {
1269
2133
  style: {
1270
2134
  display: "flex",
1271
2135
  alignItems: "center",
1272
2136
  justifyContent: "center",
1273
- height: "100%"
2137
+ height: "100%",
2138
+ flex: 1
1274
2139
  },
1275
- children: "Loading messages..."
2140
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(LoadingComponent, { text: "Loading messages..." })
1276
2141
  }
1277
2142
  );
1278
2143
  }
1279
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
1280
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: `
2144
+ const getGroupStyle = (index) => {
2145
+ const msg = messages[index];
2146
+ const prev = index > 0 ? messages[index - 1] : null;
2147
+ const next = index < messages.length - 1 ? messages[index + 1] : null;
2148
+ const sameSenderPrev = prev && prev.senderId === msg.senderId && !prev.isDeleted && isSameDay(prev.createdAt, msg.createdAt);
2149
+ const sameSenderNext = next && next.senderId === msg.senderId && !next.isDeleted && isSameDay(next.createdAt, msg.createdAt);
2150
+ if (sameSenderPrev && sameSenderNext) return "middle";
2151
+ if (sameSenderPrev) return "bottom";
2152
+ if (sameSenderNext) return "top";
2153
+ return "single";
2154
+ };
2155
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
2156
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("style", { children: `
1281
2157
  @keyframes hermes-bounce {
1282
2158
  0%, 80%, 100% { transform: translateY(0); }
1283
2159
  40% { transform: translateY(-5px); }
@@ -1287,7 +2163,7 @@ var MessageList = ({
1287
2163
  to { opacity: 1; transform: scale(1); }
1288
2164
  }
1289
2165
  ` }),
1290
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2166
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
1291
2167
  "div",
1292
2168
  {
1293
2169
  ref: containerRef,
@@ -1296,11 +2172,11 @@ var MessageList = ({
1296
2172
  overflowY: "auto",
1297
2173
  display: "flex",
1298
2174
  flexDirection: "column",
1299
- height: "100%",
2175
+ flex: 1,
1300
2176
  padding: "16px"
1301
2177
  },
1302
2178
  children: [
1303
- hasMore && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { textAlign: "center", marginBottom: 12 }, children: loadingMore ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: 12, opacity: 0.5 }, children: "Loading older messages..." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2179
+ hasMore && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { textAlign: "center", marginBottom: 12 }, children: loadingMore ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(LoadingComponent, { size: 20, text: "Loading older messages..." }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1304
2180
  "button",
1305
2181
  {
1306
2182
  onClick: onLoadMore,
@@ -1315,35 +2191,32 @@ var MessageList = ({
1315
2191
  children: "Load older messages"
1316
2192
  }
1317
2193
  ) }),
1318
- messages.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1319
- "div",
1320
- {
1321
- style: {
1322
- textAlign: "center",
1323
- opacity: 0.4,
1324
- margin: "auto",
1325
- fontSize: 14
1326
- },
1327
- children: "No messages yet. Say hello! \u{1F44B}"
1328
- }
1329
- ),
1330
- messages.map((message) => {
1331
- const isOwn = message.senderId === currentUser.userId;
1332
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 8 }, children: renderMessage ? renderMessage(message, isOwn) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1333
- DefaultMessage,
1334
- {
1335
- message,
1336
- isOwn,
1337
- onEdit,
1338
- onDelete,
1339
- onReact,
1340
- onReply,
1341
- renderAvatar
1342
- }
1343
- ) }, message._id);
2194
+ messages.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(EmptyComponent, { listType: "message" }),
2195
+ messages.map((message, index) => {
2196
+ const isOwn = message.senderId === currentUser?.userId;
2197
+ const groupStyle = getGroupStyle(index);
2198
+ const showDateSep = !disableDateSeparator && (index === 0 || !isSameDay(messages[index - 1].createdAt, message.createdAt));
2199
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react17.default.Fragment, { children: [
2200
+ showDateSep && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(DateSepComponent, { date: new Date(message.createdAt) }),
2201
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { style: { marginBottom: groupStyle === "bottom" || groupStyle === "single" ? 8 : 2 }, children: renderMessage ? renderMessage(message, isOwn) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
2202
+ MessageComponent,
2203
+ {
2204
+ message,
2205
+ isOwn,
2206
+ onEdit,
2207
+ onDelete,
2208
+ onReact,
2209
+ onReply,
2210
+ onOpenThread,
2211
+ renderAvatar,
2212
+ groupStyle,
2213
+ showAvatar: true
2214
+ }
2215
+ ) })
2216
+ ] }, message._id);
1344
2217
  }),
1345
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TypingIndicator, { typingUsers }),
1346
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: bottomRef })
2218
+ typingText && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TypingComponent, { typingText }),
2219
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { ref: bottomRef })
1347
2220
  ]
1348
2221
  }
1349
2222
  )
@@ -1351,28 +2224,34 @@ var MessageList = ({
1351
2224
  };
1352
2225
 
1353
2226
  // src/react/components/ChatInput.tsx
1354
- var import_react9 = require("react");
1355
- var import_jsx_runtime2 = require("react/jsx-runtime");
1356
- var ChatInput = ({
1357
- onSendText,
1358
- onSendFile,
1359
- onTypingStart,
1360
- onTypingStop,
1361
- replyingTo,
1362
- onCancelReply,
1363
- disabled = false,
1364
- placeholder = "Type a message...",
1365
- maxLength = 4e3,
1366
- className = "",
1367
- inputClassName = "",
1368
- renderAttachIcon,
1369
- renderSendIcon
1370
- }) => {
1371
- const [text, setText] = (0, import_react9.useState)("");
1372
- const [sending, setSending] = (0, import_react9.useState)(false);
1373
- const fileRef = (0, import_react9.useRef)(null);
1374
- const textareaRef = (0, import_react9.useRef)(null);
1375
- const resizeTextarea = (0, import_react9.useCallback)(() => {
2227
+ var import_react18 = require("react");
2228
+ var import_jsx_runtime19 = require("react/jsx-runtime");
2229
+ var ChatInput = (props) => {
2230
+ const roomActionCtx = useRoomActionContext("ChatInput");
2231
+ const typingCtx = useTypingContext("ChatInput");
2232
+ const chatCtx = useChatContext("ChatInput");
2233
+ const onSendText = props.onSendText ?? (roomActionCtx.sendMessage ? async (text2) => {
2234
+ await roomActionCtx.sendMessage({ type: "text", text: text2 });
2235
+ } : void 0);
2236
+ const onSendFile = props.onSendFile;
2237
+ const onTypingStart = props.onTypingStart ?? typingCtx.startTyping;
2238
+ const onTypingStop = props.onTypingStop ?? typingCtx.stopTyping;
2239
+ const {
2240
+ replyingTo,
2241
+ onCancelReply,
2242
+ disabled = false,
2243
+ placeholder = "Type a message...",
2244
+ maxLength = 4e3,
2245
+ className = "",
2246
+ inputClassName = "",
2247
+ renderAttachIcon,
2248
+ renderSendIcon
2249
+ } = props;
2250
+ const [text, setText] = (0, import_react18.useState)("");
2251
+ const [sending, setSending] = (0, import_react18.useState)(false);
2252
+ const fileRef = (0, import_react18.useRef)(null);
2253
+ const textareaRef = (0, import_react18.useRef)(null);
2254
+ const resizeTextarea = (0, import_react18.useCallback)(() => {
1376
2255
  const el = textareaRef.current;
1377
2256
  if (!el) return;
1378
2257
  el.style.height = "auto";
@@ -1385,7 +2264,7 @@ var ChatInput = ({
1385
2264
  };
1386
2265
  const handleSend = async () => {
1387
2266
  const trimmed = text.trim();
1388
- if (!trimmed || sending || disabled) return;
2267
+ if (!trimmed || sending || disabled || !onSendText) return;
1389
2268
  setSending(true);
1390
2269
  try {
1391
2270
  await onSendText(trimmed);
@@ -1408,7 +2287,7 @@ var ChatInput = ({
1408
2287
  await onSendFile(file);
1409
2288
  if (fileRef.current) fileRef.current.value = "";
1410
2289
  };
1411
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2290
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
1412
2291
  "div",
1413
2292
  {
1414
2293
  className: `hermes-chat-input ${className}`,
@@ -1419,7 +2298,7 @@ var ChatInput = ({
1419
2298
  borderTop: "1px solid #e0e0e0"
1420
2299
  },
1421
2300
  children: [
1422
- replyingTo && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2301
+ replyingTo && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
1423
2302
  "div",
1424
2303
  {
1425
2304
  className: "hermes-chat-input__reply",
@@ -1435,11 +2314,11 @@ var ChatInput = ({
1435
2314
  fontSize: 12
1436
2315
  },
1437
2316
  children: [
1438
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { overflow: "hidden" }, children: [
1439
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { fontWeight: 600, marginRight: 4 }, children: "Replying to:" }),
1440
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { opacity: 0.7 }, children: replyingTo.type === "text" ? replyingTo.text?.slice(0, 60) : `[${replyingTo.type}]` })
2317
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { overflow: "hidden" }, children: [
2318
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: { fontWeight: 600, marginRight: 4 }, children: "Replying to:" }),
2319
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { style: { opacity: 0.7 }, children: replyingTo.type === "text" ? replyingTo.text?.slice(0, 60) : `[${replyingTo.type}]` })
1441
2320
  ] }),
1442
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2321
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1443
2322
  "button",
1444
2323
  {
1445
2324
  onClick: onCancelReply,
@@ -1456,14 +2335,14 @@ var ChatInput = ({
1456
2335
  ]
1457
2336
  }
1458
2337
  ),
1459
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2338
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
1460
2339
  "div",
1461
2340
  {
1462
2341
  className: "hermes-chat-input__row",
1463
2342
  style: { display: "flex", alignItems: "flex-end", gap: 8 },
1464
2343
  children: [
1465
- onSendFile && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
1466
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2344
+ onSendFile && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
2345
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1467
2346
  "button",
1468
2347
  {
1469
2348
  onClick: () => fileRef.current?.click(),
@@ -1477,23 +2356,10 @@ var ChatInput = ({
1477
2356
  flexShrink: 0,
1478
2357
  opacity: disabled ? 0.4 : 1
1479
2358
  },
1480
- children: renderAttachIcon ? renderAttachIcon() : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1481
- "svg",
1482
- {
1483
- width: "20",
1484
- height: "20",
1485
- viewBox: "0 0 24 24",
1486
- fill: "none",
1487
- stroke: "currentColor",
1488
- strokeWidth: "2",
1489
- strokeLinecap: "round",
1490
- strokeLinejoin: "round",
1491
- children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48" })
1492
- }
1493
- )
2359
+ children: renderAttachIcon ? renderAttachIcon() : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("path", { d: "m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48" }) })
1494
2360
  }
1495
2361
  ),
1496
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2362
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1497
2363
  "input",
1498
2364
  {
1499
2365
  ref: fileRef,
@@ -1504,7 +2370,7 @@ var ChatInput = ({
1504
2370
  }
1505
2371
  )
1506
2372
  ] }),
1507
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2373
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1508
2374
  "textarea",
1509
2375
  {
1510
2376
  ref: textareaRef,
@@ -1531,7 +2397,7 @@ var ChatInput = ({
1531
2397
  }
1532
2398
  }
1533
2399
  ),
1534
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2400
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1535
2401
  "button",
1536
2402
  {
1537
2403
  onClick: handleSend,
@@ -1545,35 +2411,24 @@ var ChatInput = ({
1545
2411
  flexShrink: 0,
1546
2412
  opacity: !text.trim() || sending || disabled ? 0.4 : 1
1547
2413
  },
1548
- children: renderSendIcon ? renderSendIcon() : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" }) })
2414
+ children: renderSendIcon ? renderSendIcon() : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("path", { d: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" }) })
1549
2415
  }
1550
2416
  )
1551
2417
  ]
1552
2418
  }
1553
2419
  ),
1554
- text.length > maxLength * 0.8 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1555
- "div",
1556
- {
1557
- style: {
1558
- fontSize: 10,
1559
- textAlign: "right",
1560
- opacity: 0.5,
1561
- marginTop: 2
1562
- },
1563
- children: [
1564
- text.length,
1565
- "/",
1566
- maxLength
1567
- ]
1568
- }
1569
- )
2420
+ text.length > maxLength * 0.8 && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { style: { fontSize: 10, textAlign: "right", opacity: 0.5, marginTop: 2 }, children: [
2421
+ text.length,
2422
+ "/",
2423
+ maxLength
2424
+ ] })
1570
2425
  ]
1571
2426
  }
1572
2427
  );
1573
2428
  };
1574
2429
 
1575
2430
  // src/react/components/RoomList.tsx
1576
- var import_jsx_runtime3 = require("react/jsx-runtime");
2431
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1577
2432
  var formatLastActivity = (iso) => {
1578
2433
  const date = new Date(iso);
1579
2434
  const now = /* @__PURE__ */ new Date();
@@ -1604,7 +2459,7 @@ var getLastMessagePreview = (room) => {
1604
2459
  if (msg.type === "link") return `\u{1F517} ${msg.url}`;
1605
2460
  return "";
1606
2461
  };
1607
- var DefaultRoomItem = ({ room, isActive, currentUserId, renderAvatar, itemClassName }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2462
+ var DefaultRoomItem = ({ room, isActive, currentUserId, renderAvatar, itemClassName }) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1608
2463
  "div",
1609
2464
  {
1610
2465
  className: `hermes-room-item ${isActive ? "hermes-room-item--active" : ""} ${itemClassName ?? ""}`,
@@ -1618,7 +2473,7 @@ var DefaultRoomItem = ({ room, isActive, currentUserId, renderAvatar, itemClassN
1618
2473
  borderLeft: isActive ? "3px solid #0084ff" : "3px solid transparent"
1619
2474
  },
1620
2475
  children: [
1621
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { flexShrink: 0 }, children: renderAvatar ? renderAvatar(room) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2476
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { flexShrink: 0 }, children: renderAvatar ? renderAvatar(room) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1622
2477
  "div",
1623
2478
  {
1624
2479
  style: {
@@ -1635,8 +2490,8 @@ var DefaultRoomItem = ({ room, isActive, currentUserId, renderAvatar, itemClassN
1635
2490
  children: room.type === "group" ? "G" : "D"
1636
2491
  }
1637
2492
  ) }),
1638
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { flex: 1, overflow: "hidden" }, children: [
1639
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2493
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { style: { flex: 1, overflow: "hidden" }, children: [
2494
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1640
2495
  "div",
1641
2496
  {
1642
2497
  style: {
@@ -1645,7 +2500,7 @@ var DefaultRoomItem = ({ room, isActive, currentUserId, renderAvatar, itemClassN
1645
2500
  alignItems: "baseline"
1646
2501
  },
1647
2502
  children: [
1648
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2503
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1649
2504
  "span",
1650
2505
  {
1651
2506
  style: {
@@ -1658,7 +2513,7 @@ var DefaultRoomItem = ({ room, isActive, currentUserId, renderAvatar, itemClassN
1658
2513
  children: getRoomName(room, currentUserId)
1659
2514
  }
1660
2515
  ),
1661
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2516
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1662
2517
  "span",
1663
2518
  {
1664
2519
  style: { fontSize: 11, opacity: 0.5, flexShrink: 0, marginLeft: 4 },
@@ -1668,7 +2523,7 @@ var DefaultRoomItem = ({ room, isActive, currentUserId, renderAvatar, itemClassN
1668
2523
  ]
1669
2524
  }
1670
2525
  ),
1671
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2526
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1672
2527
  "div",
1673
2528
  {
1674
2529
  style: {
@@ -1678,7 +2533,7 @@ var DefaultRoomItem = ({ room, isActive, currentUserId, renderAvatar, itemClassN
1678
2533
  marginTop: 2
1679
2534
  },
1680
2535
  children: [
1681
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2536
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1682
2537
  "span",
1683
2538
  {
1684
2539
  style: {
@@ -1691,7 +2546,7 @@ var DefaultRoomItem = ({ room, isActive, currentUserId, renderAvatar, itemClassN
1691
2546
  children: getLastMessagePreview(room)
1692
2547
  }
1693
2548
  ),
1694
- room.unreadCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2549
+ room.unreadCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1695
2550
  "span",
1696
2551
  {
1697
2552
  style: {
@@ -1728,7 +2583,7 @@ var RoomList = ({
1728
2583
  className = "",
1729
2584
  itemClassName = ""
1730
2585
  }) => {
1731
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2586
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1732
2587
  "div",
1733
2588
  {
1734
2589
  className: `hermes-room-list ${className}`,
@@ -1739,7 +2594,7 @@ var RoomList = ({
1739
2594
  overflowY: "auto"
1740
2595
  },
1741
2596
  children: [
1742
- (onCreateDirect || onCreateGroup) && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2597
+ (onCreateDirect || onCreateGroup) && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1743
2598
  "div",
1744
2599
  {
1745
2600
  style: {
@@ -1749,7 +2604,7 @@ var RoomList = ({
1749
2604
  borderBottom: "1px solid #e0e0e0"
1750
2605
  },
1751
2606
  children: [
1752
- onCreateDirect && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2607
+ onCreateDirect && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1753
2608
  "button",
1754
2609
  {
1755
2610
  onClick: onCreateDirect,
@@ -1767,7 +2622,7 @@ var RoomList = ({
1767
2622
  children: "+ Direct"
1768
2623
  }
1769
2624
  ),
1770
- onCreateGroup && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2625
+ onCreateGroup && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1771
2626
  "button",
1772
2627
  {
1773
2628
  onClick: onCreateGroup,
@@ -1787,8 +2642,8 @@ var RoomList = ({
1787
2642
  ]
1788
2643
  }
1789
2644
  ),
1790
- loading && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { padding: "12px 16px", opacity: 0.5, fontSize: 13 }, children: "Loading rooms..." }),
1791
- !loading && rooms.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2645
+ loading && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { style: { padding: "12px 16px", opacity: 0.5, fontSize: 13 }, children: "Loading rooms..." }),
2646
+ !loading && rooms.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1792
2647
  "div",
1793
2648
  {
1794
2649
  style: {
@@ -1802,7 +2657,7 @@ var RoomList = ({
1802
2657
  ),
1803
2658
  !loading && rooms.map((room) => {
1804
2659
  const isActive = room._id === activeRoomId;
1805
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { onClick: () => onSelectRoom(room), children: renderRoomItem ? renderRoomItem(room, isActive) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2660
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { onClick: () => onSelectRoom(room), children: renderRoomItem ? renderRoomItem(room, isActive) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
1806
2661
  DefaultRoomItem,
1807
2662
  {
1808
2663
  room,
@@ -1818,58 +2673,13 @@ var RoomList = ({
1818
2673
  );
1819
2674
  };
1820
2675
 
1821
- // src/react/components/TypingIndicator.tsx
1822
- var import_jsx_runtime4 = require("react/jsx-runtime");
1823
- var TypingIndicator2 = ({
1824
- typingText,
1825
- className = ""
1826
- }) => {
1827
- if (!typingText) return null;
1828
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1829
- "div",
1830
- {
1831
- className: `hermes-typing-indicator ${className}`,
1832
- style: {
1833
- display: "flex",
1834
- alignItems: "center",
1835
- gap: 6,
1836
- padding: "4px 16px",
1837
- minHeight: 24
1838
- },
1839
- children: [
1840
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { display: "flex", gap: 3 }, children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1841
- "span",
1842
- {
1843
- style: {
1844
- width: 6,
1845
- height: 6,
1846
- borderRadius: "50%",
1847
- background: "#aaa",
1848
- display: "block",
1849
- animation: `hermes-bounce 1.2s ease-in-out ${i * 0.2}s infinite`
1850
- }
1851
- },
1852
- i
1853
- )) }),
1854
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { fontSize: 12, opacity: 0.6 }, children: typingText }),
1855
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: `
1856
- @keyframes hermes-bounce {
1857
- 0%, 80%, 100% { transform: translateY(0); }
1858
- 40% { transform: translateY(-4px); }
1859
- }
1860
- ` })
1861
- ]
1862
- }
1863
- );
1864
- };
1865
-
1866
2676
  // src/react/components/OnlineBadge.tsx
1867
- var import_jsx_runtime5 = require("react/jsx-runtime");
2677
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1868
2678
  var OnlineBadge = ({
1869
2679
  isOnline,
1870
2680
  size = 10,
1871
2681
  className = ""
1872
- }) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2682
+ }) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1873
2683
  "span",
1874
2684
  {
1875
2685
  className: `hermes-online-badge ${isOnline ? "hermes-online-badge--online" : "hermes-online-badge--offline"} ${className}`,
@@ -1887,9 +2697,9 @@ var OnlineBadge = ({
1887
2697
  );
1888
2698
 
1889
2699
  // src/react/components/ReactionPicker.tsx
1890
- var import_react10 = require("react");
2700
+ var import_react19 = require("react");
1891
2701
  var import_emoji_picker_react = __toESM(require("emoji-picker-react"), 1);
1892
- var import_jsx_runtime6 = require("react/jsx-runtime");
2702
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1893
2703
  var DEFAULT_EMOJIS = ["\u{1F44D}", "\u2764\uFE0F", "\u{1F602}", "\u{1F62E}", "\u{1F622}", "\u{1F525}", "\u{1F389}", "\u{1F44F}"];
1894
2704
  var ReactionPicker = ({
1895
2705
  onSelect,
@@ -1899,8 +2709,8 @@ var ReactionPicker = ({
1899
2709
  className = "",
1900
2710
  align = "left"
1901
2711
  }) => {
1902
- const [showPicker, setShowPicker] = (0, import_react10.useState)(false);
1903
- const containerRef = (0, import_react10.useRef)(null);
2712
+ const [showPicker, setShowPicker] = (0, import_react19.useState)(false);
2713
+ const containerRef = (0, import_react19.useRef)(null);
1904
2714
  const hasReacted = (emoji) => {
1905
2715
  if (!currentUserId) return false;
1906
2716
  return currentReactions.find((r) => r.emoji === emoji)?.users.includes(currentUserId) ?? false;
@@ -1909,7 +2719,7 @@ var ReactionPicker = ({
1909
2719
  onSelect(emojiData.emoji);
1910
2720
  setShowPicker(false);
1911
2721
  };
1912
- (0, import_react10.useEffect)(() => {
2722
+ (0, import_react19.useEffect)(() => {
1913
2723
  const handleOutsideClick = (e) => {
1914
2724
  if (!containerRef.current) return;
1915
2725
  const target = e.target;
@@ -1922,14 +2732,14 @@ var ReactionPicker = ({
1922
2732
  }
1923
2733
  return () => window.removeEventListener("click", handleOutsideClick);
1924
2734
  }, [showPicker]);
1925
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2735
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
1926
2736
  "div",
1927
2737
  {
1928
2738
  ref: containerRef,
1929
2739
  style: { position: "relative", display: "inline-block" },
1930
2740
  className,
1931
2741
  children: [
1932
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
2742
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
1933
2743
  "div",
1934
2744
  {
1935
2745
  style: {
@@ -1942,7 +2752,7 @@ var ReactionPicker = ({
1942
2752
  border: "1px solid rgba(255,255,255,0.08)"
1943
2753
  },
1944
2754
  children: [
1945
- emojis.map((emoji) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2755
+ emojis.map((emoji) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1946
2756
  "button",
1947
2757
  {
1948
2758
  onClick: () => onSelect(emoji),
@@ -1962,7 +2772,7 @@ var ReactionPicker = ({
1962
2772
  },
1963
2773
  emoji
1964
2774
  )),
1965
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2775
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1966
2776
  "button",
1967
2777
  {
1968
2778
  onClick: (e) => {
@@ -1983,7 +2793,7 @@ var ReactionPicker = ({
1983
2793
  ]
1984
2794
  }
1985
2795
  ),
1986
- showPicker && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2796
+ showPicker && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1987
2797
  "div",
1988
2798
  {
1989
2799
  onMouseDown: (e) => e.stopPropagation(),
@@ -1995,7 +2805,7 @@ var ReactionPicker = ({
1995
2805
  zIndex: 50,
1996
2806
  animation: "hermes-pop 0.15s ease"
1997
2807
  },
1998
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2808
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1999
2809
  import_emoji_picker_react.default,
2000
2810
  {
2001
2811
  theme: import_emoji_picker_react.Theme.DARK,
@@ -2008,7 +2818,7 @@ var ReactionPicker = ({
2008
2818
  )
2009
2819
  }
2010
2820
  ),
2011
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("style", { children: `
2821
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("style", { children: `
2012
2822
  @keyframes hermes-pop {
2013
2823
  from {
2014
2824
  opacity: 0;
@@ -2026,7 +2836,7 @@ var ReactionPicker = ({
2026
2836
  };
2027
2837
 
2028
2838
  // src/react/components/MediaMessage.tsx
2029
- var import_jsx_runtime7 = require("react/jsx-runtime");
2839
+ var import_jsx_runtime23 = require("react/jsx-runtime");
2030
2840
  var formatFileSize2 = (bytes) => {
2031
2841
  if (!bytes) return "";
2032
2842
  if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
@@ -2039,13 +2849,13 @@ var MediaMessage = ({
2039
2849
  maxWidth = 300
2040
2850
  }) => {
2041
2851
  if (!message.url) return null;
2042
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2852
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2043
2853
  "div",
2044
2854
  {
2045
2855
  className: `hermes-media-message hermes-media-message--${message.type} ${className}`,
2046
2856
  style: { maxWidth },
2047
2857
  children: [
2048
- message.type === "image" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2858
+ message.type === "image" && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2049
2859
  "img",
2050
2860
  {
2051
2861
  src: message.url,
@@ -2059,7 +2869,7 @@ var MediaMessage = ({
2059
2869
  onClick: () => window.open(message.url, "_blank")
2060
2870
  }
2061
2871
  ),
2062
- message.type === "video" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2872
+ message.type === "video" && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2063
2873
  "video",
2064
2874
  {
2065
2875
  src: message.url,
@@ -2068,17 +2878,17 @@ var MediaMessage = ({
2068
2878
  style: { width: "100%", borderRadius: 10 }
2069
2879
  }
2070
2880
  ),
2071
- message.type === "audio" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2881
+ message.type === "audio" && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2072
2882
  "div",
2073
2883
  {
2074
2884
  style: { display: "flex", alignItems: "center", gap: 8, padding: 8 },
2075
2885
  children: [
2076
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontSize: 20 }, children: "\u{1F3B5}" }),
2077
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("audio", { src: message.url, controls: true, style: { flex: 1, height: 36 } })
2886
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { style: { fontSize: 20 }, children: "\u{1F3B5}" }),
2887
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("audio", { src: message.url, controls: true, style: { flex: 1, height: 36 } })
2078
2888
  ]
2079
2889
  }
2080
2890
  ),
2081
- message.type === "document" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2891
+ message.type === "document" && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2082
2892
  "a",
2083
2893
  {
2084
2894
  href: message.url,
@@ -2095,9 +2905,9 @@ var MediaMessage = ({
2095
2905
  color: "inherit"
2096
2906
  },
2097
2907
  children: [
2098
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontSize: 28, flexShrink: 0 }, children: "\u{1F4C4}" }),
2099
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { overflow: "hidden" }, children: [
2100
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2908
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { style: { fontSize: 28, flexShrink: 0 }, children: "\u{1F4C4}" }),
2909
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { overflow: "hidden" }, children: [
2910
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2101
2911
  "div",
2102
2912
  {
2103
2913
  style: {
@@ -2110,7 +2920,7 @@ var MediaMessage = ({
2110
2920
  children: message.fileName ?? "Document"
2111
2921
  }
2112
2922
  ),
2113
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { fontSize: 11, opacity: 0.6 }, children: [
2923
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { style: { fontSize: 11, opacity: 0.6 }, children: [
2114
2924
  formatFileSize2(message.fileSize),
2115
2925
  " \xB7 Click to download"
2116
2926
  ] })
@@ -2118,7 +2928,7 @@ var MediaMessage = ({
2118
2928
  ]
2119
2929
  }
2120
2930
  ),
2121
- message.type === "link" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2931
+ message.type === "link" && /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
2122
2932
  "a",
2123
2933
  {
2124
2934
  href: message.url,
@@ -2146,22 +2956,436 @@ var MediaMessage = ({
2146
2956
  }
2147
2957
  );
2148
2958
  };
2959
+
2960
+ // src/react/components/Thread/ThreadHeader.tsx
2961
+ var import_jsx_runtime24 = require("react/jsx-runtime");
2962
+ var ThreadHeader = ({
2963
+ thread,
2964
+ onClose,
2965
+ className = ""
2966
+ }) => /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
2967
+ "div",
2968
+ {
2969
+ className: `hermes-thread-header ${className}`,
2970
+ style: {
2971
+ display: "flex",
2972
+ alignItems: "center",
2973
+ justifyContent: "space-between",
2974
+ padding: "12px 16px",
2975
+ borderBottom: "1px solid rgba(128,128,128,0.15)"
2976
+ },
2977
+ children: [
2978
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { style: { flex: 1, overflow: "hidden" }, children: [
2979
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { style: { fontWeight: 700, fontSize: 15, marginBottom: 2 }, children: "Thread" }),
2980
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2981
+ "div",
2982
+ {
2983
+ style: {
2984
+ fontSize: 12,
2985
+ opacity: 0.6,
2986
+ overflow: "hidden",
2987
+ textOverflow: "ellipsis",
2988
+ whiteSpace: "nowrap"
2989
+ },
2990
+ children: thread.type === "text" ? thread.text?.slice(0, 80) : `[${thread.type}]`
2991
+ }
2992
+ )
2993
+ ] }),
2994
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2995
+ "button",
2996
+ {
2997
+ onClick: onClose,
2998
+ style: {
2999
+ background: "none",
3000
+ border: "none",
3001
+ cursor: "pointer",
3002
+ fontSize: 18,
3003
+ padding: 4,
3004
+ opacity: 0.6,
3005
+ lineHeight: 1
3006
+ },
3007
+ children: "\u2715"
3008
+ }
3009
+ )
3010
+ ]
3011
+ }
3012
+ );
3013
+
3014
+ // src/react/components/Thread/Thread.tsx
3015
+ var import_jsx_runtime25 = require("react/jsx-runtime");
3016
+ var Thread = ({
3017
+ className = "",
3018
+ autoFocus = true
3019
+ }) => {
3020
+ const { currentUser, customClasses } = useChatContext("Thread");
3021
+ const { thread, threadMessages, threadHasMore, threadLoadingMore } = useRoomStateContext("Thread");
3022
+ const { closeThread, addReaction, deleteMessage, editMessage } = useRoomActionContext("Thread");
3023
+ const {
3024
+ ThreadHeader: CustomThreadHeader,
3025
+ Message: CustomMessage
3026
+ } = useComponentContext("Thread");
3027
+ if (!thread) return null;
3028
+ const ThreadHeaderComponent = CustomThreadHeader || ThreadHeader;
3029
+ const MessageComponent = CustomMessage || Message;
3030
+ const threadClass = customClasses?.thread || `hermes-thread ${className}`.trim();
3031
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
3032
+ "div",
3033
+ {
3034
+ className: threadClass,
3035
+ style: {
3036
+ display: "flex",
3037
+ flexDirection: "column",
3038
+ height: "100%",
3039
+ borderLeft: "1px solid rgba(128,128,128,0.15)",
3040
+ minWidth: 320,
3041
+ maxWidth: 420
3042
+ },
3043
+ children: [
3044
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ThreadHeaderComponent, { thread, onClose: closeThread }),
3045
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3046
+ "div",
3047
+ {
3048
+ style: {
3049
+ padding: "12px 16px",
3050
+ borderBottom: "1px solid rgba(128,128,128,0.1)",
3051
+ background: "rgba(128,128,128,0.03)"
3052
+ },
3053
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3054
+ MessageComponent,
3055
+ {
3056
+ message: thread,
3057
+ isOwn: thread.senderId === currentUser?.userId,
3058
+ onReact: (id, emoji) => addReaction(id, emoji),
3059
+ showAvatar: true
3060
+ }
3061
+ )
3062
+ }
3063
+ ),
3064
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3065
+ "div",
3066
+ {
3067
+ style: {
3068
+ flex: 1,
3069
+ overflowY: "auto",
3070
+ padding: 16,
3071
+ display: "flex",
3072
+ flexDirection: "column"
3073
+ },
3074
+ children: threadMessages.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(EmptyStateIndicator, { listType: "thread" }) : threadMessages.map((msg) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { style: { marginBottom: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3075
+ MessageComponent,
3076
+ {
3077
+ message: msg,
3078
+ isOwn: msg.senderId === currentUser?.userId,
3079
+ onEdit: (id, text) => editMessage(id, text),
3080
+ onDelete: (id) => deleteMessage(id),
3081
+ onReact: (id, emoji) => addReaction(id, emoji),
3082
+ showAvatar: true
3083
+ }
3084
+ ) }, msg._id))
3085
+ }
3086
+ )
3087
+ ]
3088
+ }
3089
+ );
3090
+ };
3091
+
3092
+ // src/react/components/Modal/Modal.tsx
3093
+ var import_react20 = require("react");
3094
+ var import_jsx_runtime26 = require("react/jsx-runtime");
3095
+ var Modal = ({
3096
+ open,
3097
+ onClose,
3098
+ className = "",
3099
+ children
3100
+ }) => {
3101
+ const handleKeyDown = (0, import_react20.useCallback)(
3102
+ (e) => {
3103
+ if (e.key === "Escape") onClose();
3104
+ },
3105
+ [onClose]
3106
+ );
3107
+ (0, import_react20.useEffect)(() => {
3108
+ if (open) {
3109
+ document.addEventListener("keydown", handleKeyDown);
3110
+ document.body.style.overflow = "hidden";
3111
+ }
3112
+ return () => {
3113
+ document.removeEventListener("keydown", handleKeyDown);
3114
+ document.body.style.overflow = "";
3115
+ };
3116
+ }, [open, handleKeyDown]);
3117
+ if (!open) return null;
3118
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
3119
+ "div",
3120
+ {
3121
+ className: `hermes-modal-overlay ${className}`,
3122
+ onClick: onClose,
3123
+ style: {
3124
+ position: "fixed",
3125
+ inset: 0,
3126
+ zIndex: 9999,
3127
+ display: "flex",
3128
+ alignItems: "center",
3129
+ justifyContent: "center",
3130
+ background: "rgba(0,0,0,0.6)",
3131
+ backdropFilter: "blur(4px)",
3132
+ animation: "hermes-fade-in 0.15s ease"
3133
+ },
3134
+ children: [
3135
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
3136
+ "div",
3137
+ {
3138
+ className: "hermes-modal-content",
3139
+ onClick: (e) => e.stopPropagation(),
3140
+ style: {
3141
+ maxWidth: "90vw",
3142
+ maxHeight: "90vh",
3143
+ overflow: "auto",
3144
+ borderRadius: 12,
3145
+ background: "#fff",
3146
+ boxShadow: "0 16px 64px rgba(0,0,0,0.3)",
3147
+ animation: "hermes-pop 0.2s ease"
3148
+ },
3149
+ children: [
3150
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3151
+ "button",
3152
+ {
3153
+ onClick: onClose,
3154
+ style: {
3155
+ position: "absolute",
3156
+ top: 16,
3157
+ right: 16,
3158
+ background: "rgba(0,0,0,0.5)",
3159
+ color: "#fff",
3160
+ border: "none",
3161
+ borderRadius: "50%",
3162
+ width: 32,
3163
+ height: 32,
3164
+ cursor: "pointer",
3165
+ fontSize: 16,
3166
+ display: "flex",
3167
+ alignItems: "center",
3168
+ justifyContent: "center",
3169
+ zIndex: 1
3170
+ },
3171
+ children: "\u2715"
3172
+ }
3173
+ ),
3174
+ children
3175
+ ]
3176
+ }
3177
+ ),
3178
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("style", { children: `
3179
+ @keyframes hermes-fade-in {
3180
+ from { opacity: 0; }
3181
+ to { opacity: 1; }
3182
+ }
3183
+ @keyframes hermes-pop {
3184
+ from { opacity: 0; transform: scale(0.9); }
3185
+ to { opacity: 1; transform: scale(1); }
3186
+ }
3187
+ ` })
3188
+ ]
3189
+ }
3190
+ );
3191
+ };
3192
+
3193
+ // src/react/components/Search/Search.tsx
3194
+ var import_react21 = require("react");
3195
+ var import_jsx_runtime27 = require("react/jsx-runtime");
3196
+ var Search = ({
3197
+ messages = [],
3198
+ onSelectResult,
3199
+ placeholder = "Search messages...",
3200
+ className = ""
3201
+ }) => {
3202
+ const [query, setQuery] = (0, import_react21.useState)("");
3203
+ const [focused, setFocused] = (0, import_react21.useState)(false);
3204
+ const results = (0, import_react21.useMemo)(() => {
3205
+ if (!query.trim()) return [];
3206
+ const lower = query.toLowerCase();
3207
+ return messages.filter(
3208
+ (m) => !m.isDeleted && m.type === "text" && m.text?.toLowerCase().includes(lower)
3209
+ ).slice(0, 20);
3210
+ }, [query, messages]);
3211
+ const handleSelect = (0, import_react21.useCallback)(
3212
+ (msg) => {
3213
+ onSelectResult?.(msg);
3214
+ setQuery("");
3215
+ setFocused(false);
3216
+ },
3217
+ [onSelectResult]
3218
+ );
3219
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
3220
+ "div",
3221
+ {
3222
+ className: `hermes-search ${className}`,
3223
+ style: { position: "relative" },
3224
+ children: [
3225
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
3226
+ "div",
3227
+ {
3228
+ style: {
3229
+ display: "flex",
3230
+ alignItems: "center",
3231
+ gap: 8,
3232
+ padding: "6px 12px",
3233
+ border: "1px solid rgba(128,128,128,0.2)",
3234
+ borderRadius: 10,
3235
+ background: focused ? "#fff" : "rgba(128,128,128,0.05)",
3236
+ transition: "background 0.15s, border-color 0.15s",
3237
+ borderColor: focused ? "#0084ff" : "rgba(128,128,128,0.2)"
3238
+ },
3239
+ children: [
3240
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { style: { fontSize: 14, opacity: 0.5 }, children: "\u{1F50D}" }),
3241
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3242
+ "input",
3243
+ {
3244
+ type: "text",
3245
+ value: query,
3246
+ onChange: (e) => setQuery(e.target.value),
3247
+ onFocus: () => setFocused(true),
3248
+ onBlur: () => setTimeout(() => setFocused(false), 200),
3249
+ placeholder,
3250
+ style: {
3251
+ flex: 1,
3252
+ border: "none",
3253
+ outline: "none",
3254
+ fontSize: 13,
3255
+ background: "transparent"
3256
+ }
3257
+ }
3258
+ ),
3259
+ query && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3260
+ "button",
3261
+ {
3262
+ onClick: () => setQuery(""),
3263
+ style: {
3264
+ background: "none",
3265
+ border: "none",
3266
+ cursor: "pointer",
3267
+ fontSize: 14,
3268
+ opacity: 0.5,
3269
+ lineHeight: 1
3270
+ },
3271
+ children: "\u2715"
3272
+ }
3273
+ )
3274
+ ]
3275
+ }
3276
+ ),
3277
+ focused && query.trim() && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3278
+ "div",
3279
+ {
3280
+ style: {
3281
+ position: "absolute",
3282
+ top: "calc(100% + 4px)",
3283
+ left: 0,
3284
+ right: 0,
3285
+ zIndex: 50,
3286
+ background: "#fff",
3287
+ border: "1px solid rgba(128,128,128,0.15)",
3288
+ borderRadius: 10,
3289
+ boxShadow: "0 8px 32px rgba(0,0,0,0.12)",
3290
+ maxHeight: 300,
3291
+ overflowY: "auto"
3292
+ },
3293
+ children: results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3294
+ "div",
3295
+ {
3296
+ style: {
3297
+ padding: 16,
3298
+ textAlign: "center",
3299
+ fontSize: 13,
3300
+ opacity: 0.5
3301
+ },
3302
+ children: "No results found"
3303
+ }
3304
+ ) : results.map((msg) => /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
3305
+ "div",
3306
+ {
3307
+ onClick: () => handleSelect(msg),
3308
+ style: {
3309
+ padding: "8px 12px",
3310
+ cursor: "pointer",
3311
+ borderBottom: "1px solid rgba(128,128,128,0.08)",
3312
+ transition: "background 0.1s"
3313
+ },
3314
+ onMouseEnter: (e) => e.currentTarget.style.background = "rgba(0,132,255,0.05)",
3315
+ onMouseLeave: (e) => e.currentTarget.style.background = "transparent",
3316
+ children: [
3317
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3318
+ "div",
3319
+ {
3320
+ style: {
3321
+ fontSize: 13,
3322
+ overflow: "hidden",
3323
+ textOverflow: "ellipsis",
3324
+ whiteSpace: "nowrap"
3325
+ },
3326
+ children: msg.text
3327
+ }
3328
+ ),
3329
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { style: { fontSize: 11, opacity: 0.5, marginTop: 2 }, children: new Date(msg.createdAt).toLocaleString() })
3330
+ ]
3331
+ },
3332
+ msg._id
3333
+ ))
3334
+ }
3335
+ )
3336
+ ]
3337
+ }
3338
+ );
3339
+ };
2149
3340
  // Annotate the CommonJS export names for ESM import in node:
2150
3341
  0 && (module.exports = {
3342
+ Avatar,
3343
+ Chat,
3344
+ ChatContext,
2151
3345
  ChatInput,
3346
+ ChatProvider,
3347
+ ComponentContext,
3348
+ ComponentProvider,
3349
+ DateSeparator,
3350
+ EmptyStateIndicator,
2152
3351
  HermesClient,
3352
+ LoadingErrorIndicator,
3353
+ LoadingIndicator,
2153
3354
  MediaMessage,
3355
+ Message,
3356
+ MessageActions,
3357
+ MessageContext,
2154
3358
  MessageList,
3359
+ MessageProvider,
3360
+ MessageStatus,
3361
+ Modal,
2155
3362
  OnlineBadge,
2156
3363
  ReactionPicker,
3364
+ Room,
3365
+ RoomActionContext,
3366
+ RoomActionProvider,
2157
3367
  RoomList,
3368
+ RoomStateContext,
3369
+ RoomStateProvider,
3370
+ Search,
3371
+ Thread,
3372
+ ThreadHeader,
3373
+ TypingContext,
2158
3374
  TypingIndicator,
3375
+ TypingProvider,
3376
+ Window,
3377
+ useChatContext,
3378
+ useComponentContext,
3379
+ useMessageContext,
2159
3380
  useMessages,
2160
3381
  usePresence,
2161
3382
  useReactions,
2162
3383
  useReadReceipts,
3384
+ useRoomActionContext,
3385
+ useRoomStateContext,
2163
3386
  useRooms,
2164
3387
  useTyping,
3388
+ useTypingContext,
2165
3389
  useUpload
2166
3390
  });
2167
3391
  //# sourceMappingURL=react.cjs.map