hermes-chat-react 0.1.2 → 0.1.4

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